
/*    HTTP Host:  static.ak.fbcdn.net                                          */
/*    Generated:  July 5th 2009 7:54:49 PM PDT                                 */
/*      Machine:  10.16.139.103                                                */
/*       Source:  Backing Store                                                */
/*     Location:  js/afdhsu2odxc048cc.pkg.js h:8wmbzw55                        */
/*       Locale:  en_US                                                        */
/*         Path:  js/afdhsu2odxc048cc.pkg.js                                   */
function ChatTabActions(container) {
	this._container = container;
	this._actions = {};
	this._actionOrder = [];
	this._visibilityChanged = false;
	this._anyVisible = false;
	this.actionClass = "action";
}
copy_properties(ChatTabActions.prototype, {anyVisible:function () {
	return this._anyVisible;
}, appendAction:function (name, create_element, action) {
	this._addAction(name, create_element, action);
	this._actionOrder.push(name);
	return this;
}, prependAction:function (name, create_element, action) {
	this._addAction(name, create_element, action);
	this._actionOrder.unshift(name);
	return this;
}, setVisible:function (name, visible) {
	var action = this._actions[name];
	if (action.visible != visible) {
		action.visible = visible;
		this._visibilityChanged = true;
	}
	return this;
}, refresh:function () {
	if (this._visibilityChanged) {
		this._render();
		this._visibilityChanged = false;
		return true;
	}
	return false;
}, _render:function () {
	DOM.empty(this._container);
	var actions = this._actions;
	var actionOrder = this._actionOrder;
	this._anyVisible = false;
	var elements = [];
	for (var i = 0; i < actionOrder.length; ++i) {
		var action = actions[actionOrder[i]];
		if (action.visible) {
			if (this._anyVisible) {
				elements.push($N("span", {className:"divider"}, "|"));
			}
			elements.push(action.create_element());
			this._anyVisible = true;
		}
	}
	DOM.appendContent(this._container, elements);
	this._container.style.display = this._anyVisible ? "block" : "none";
}, _addAction:function (name, create_element, action) {
	if (typeof create_element == "string") {
		var text = create_element;
		create_element = function () {
			return $N("span", {className:this.actionClass}, text);
		}.bind(this);
	}
	this._actions[name] = {create_element:function () {
		var element = create_element();
		Event.listen(element, "click", action);
		return element;
	}, visible:false};
	this._visibilityChanged = true;
}});
var Sound = (function () {
	var _initialized = false;
	var _swf_id = "so_sound_player";
	return {init:function () {
		if (_initialized) {
			return;
		}
		_initialized = true;
		var holder = document.createElement("div");
		holder.id = "sound_player_holder";
		document.body.appendChild(holder);
		try {
			var player = new SWFObject("/swf/SoundPlayer.swf", _swf_id, "1px", "1px", ["9.0.159.0", "10.0.22.87"], "#ffffff");
			player.addParam("allowscriptaccess", "always");
			player.addParam("wmode", "transparent");
			player.addVariable("swf_id", _swf_id);
			player.fallback_html = " ";
			player.write(holder.id);
			window[_swf_id] = player;
		}
		catch (swfex) {
		}
	}, play:function (path) {
		Sound.init();
		var uri = URI(path);
		if (!uri.getDomain()) {
			path = URI(env_get("static_base")).setPath(uri.getPath()).toString();
		}
		var player = document[_swf_id] || window[_swf_id];
		var embed;
		if (/\.mp3$/.test(path)) {
			if (player) {
				if (!player.playSound && player.length) {
					player = player[0];
				}
				if (player.playSound) {
					player.playSound(path);
					return;
				}
			}
		}
		embed = ge("sound");
		if (!embed) {
			embed = document.createElement("span");
			embed.setAttribute("id", "sound");
			DOM.getRootElement().appendChild(embed);
		}
		embed.innerHTML = "<embed src=\"" + path + "\" autostart=\"true\" loop=\"false\" " + "hidden=\"true\" />";
	}};
})();
function html_wordwrap(str, wrap_limit, txt_fn) {
	if (typeof wrap_limit == "undefined") {
		wrap_limit = 60;
	}
	if (typeof txt_fn != "function") {
		txt_fn = htmlize;
	}
	var regex = new RegExp("\\S{" + (wrap_limit + 1) + "}", "g");
	var start = 0;
	var str_remaining = str;
	var ret_arr = [];
	var matches = str.match(regex);
	if (matches) {
		for (var i = 0; i < matches.length; i++) {
			var match = matches[i];
			var match_index = start + str_remaining.indexOf(match);
			var chunk = str.substring(start, match_index);
			if (chunk) {
				ret_arr.push(txt_fn(chunk));
			}
			ret_arr.push(txt_fn(match) + "<wbr/>");
			start = match_index + match.length;
			str_remaining = str.substring(start);
		}
	}
	if (str_remaining) {
		ret_arr.push(txt_fn(str_remaining));
	}
	return ret_arr.join("");
}
function text_get_hyperlinks(str) {
	if (typeof (str) != "string") {
		return [];
	}
	return str.match(/(?:(?:ht|f)tps?):\/\/[^\s<]*[^\s<\.)]/ig);
}
function html_hyperlink(str, txt_fn, url_fn, reroute) {
	var accepted_delims = {"<":">", "*":"*", "{":"}", "[":"]", "'":"'", "\"":"\"", "#":"#", "+":"+", "-":"-", "(":")"};
	if (typeof (str) == "undefined" || !str.toString) {
		return "";
	}
	if (typeof txt_fn != "function") {
		txt_fn = htmlize;
	}
	if (typeof url_fn != "function") {
		url_fn = htmlize;
	}
	var str = str.toString();
	var http_matches = text_get_hyperlinks(str);
	var start = 0;
	var str_remaining = str;
	var ret_arr = [];
	var str_remaining = str;
	if (http_matches) {
		var post_form_elem = reroute ? ge("post_form_id") : null;
		var post_form_id = post_form_elem ? post_form_elem.value : "";
		for (var i = 0; i < http_matches.length; i++) {
			var http_url = http_matches[i];
			var http_index = start + str_remaining.indexOf(http_url);
			var str_len = http_url.length;
			var non_url = str.substring(start, http_index);
			if (non_url) {
				ret_arr.push(txt_fn(non_url));
			}
			var trailing = "";
			if (http_index > 0) {
				var delim = str[http_index - 1];
				if (typeof accepted_delims[delim] != "undefined") {
					var end_delim = accepted_delims[delim];
					var end_delim_index = http_url.indexOf(end_delim);
					if (end_delim_index != -1) {
						trailing = txt_fn(http_url.substring(end_delim_index));
						http_url = http_url.substring(0, end_delim_index);
					}
				}
			}
			var http_str = url_fn(http_url);
			if (reroute) {
				var http_url_quote_escape = "http://www.facebook.com/l.php?u=" + URI.encodeComponent(http_url) + "&h=" + post_form_id;
			} else {
				var http_url_quote_escape = http_url.replace(/"/g, "%22");
			}
			ret_arr.push("<a href=\"" + http_url_quote_escape + "\" target=\"_blank\" rel=\"nofollow\">" + http_str + "</a>" + trailing);
			start = http_index + str_len;
			str_remaining = str.substring(start);
		}
	}
	if (str_remaining) {
		ret_arr.push(txt_fn(str_remaining));
	}
	return ret_arr.join("");
}
function nl2br(text) {
	if (typeof (text) == "undefined" || !text.toString) {
		return "";
	}
	return text.toString().replace(/\n/g, "<br />");
}
function is_email(email) {
	return /^([\w!.%+\-])+@([\w\-])+(?:\.[\w\-]+)+$/.test(email);
}
function ChatTab(chatDisplay, id, name, firstName, numMissed, missedTime) {
	this.chatDisplay = chatDisplay;
	this.id = id;
	this.name = name;
	this.messageTypes = {"msg":{visible:true, user:true, preserveHistory:false}, "mf":{visible:true, user:false, preserveHistory:true}, "online":{visible:true, user:false, preserveHistory:true}};
	this.arbiter = new Arbiter();
	Arbiter.inform(ChatTab.GLOBALMESSAGETYPE.NEWTAB, {tab:this});
	this.tabRef = "chatDisplay.tabs[" + this.id + "]";
	this.firstName = firstName;
	this.tabDisabled = false;
	this.numMissed = numMissed;
	this.missedTime = missedTime;
	this.focused = false;
	this.lastLogItem = null;
	this.historyLoaded = false;
	this.pendingSentMsgs = [];
	this.failedSentMsgs = [];
	this.sendingDisplayMsgID = null;
	this.historyRequestID = 0;
	this.bounceAnimation = null;
	this.convTextProcessor = this._processConvText.bind(this);
	this.convTextEmoteProcessor = this._processConvTextEmote.bind(this);
	this.statusTextProcessor = this._processStatusText.bind(this);
	this.minTextHeight = presence.inPopoutWindow ? this.minTextHeightPopout : this.minTextHeightPopin;
	this.typingState = this.INACTIVE;
	this.typingRemoteState = this.INACTIVE;
	this.typingLastKeystrokeAt = null;
	this.typingNotifyTimer = null;
	this.typingCheckTimer = null;
	this.lastMessageAt = null;
	this.lastMessageHadOfflineResponse = false;
	this._buildUI();
	this.addPopoutChat(id);
	this.loadData();
	this.handleVisibility(true);
	Sound.init();
	if (presence.inVideoChat) {
		this.videochat_swf = ge(CallCenter.call(this.id)._callSWFID());
	}
}
copy_properties(ChatTab, {GLOBALMESSAGETYPE:{NEWTAB:"chattabs/newtab"}, TABMESSAGETYPE:{HISTORYBEGIN:"chattabs/historybegin", HISTORYITER:"chattabs/historyiter", HISTORYEND:"chattabs/historyend", CREATETAB:"chattabs/createtab", RECEIVEMSG:"chattabs/receivemsg", DISPLAYCHAT:"chattabs/displaychat", USERINFOUPDATED:"chattabs/userinfoupdated"}});
copy_properties(ChatTab.prototype, {pendingToLogCompareWindow:60000, sendingCheckDelay:55000, sendingDisplayDelay:4000, convWrapLimit:30, statusWrapLimit:19, handleWidth:136, popinWidth:226, popinHeight:250, popoutWidthOffset:180, flPopoutWidthOffset:200, minTextHeightPopin:13, minTextHeightPopout:26, maxTextHeight:77, msgBunchTime:60000, maxHandleLen:16, maxTitleLen:20, bounceDuration:50, bounceOrgPosition:-3, typingNotifyDelay:1000, typingKeystrokeExpiry:7000, INACTIVE:0, TYPING:1, isTabVisible:function () {
	return this.focused && (presence.inPopoutWindow || !presence.poppedOut) && (this.chatInfo.clientWidth > 20);
}, start:function () {
	this._popSendQueue();
}, restart:function () {
	this.getHistory(true);
	this.handleResize.bind(this).defer();
}, loadData:function () {
	if (this.chatDisplay.userInfos[this.id]) {
		this.updateUserInfo();
		this.arbiter.inform(ChatTab.TABMESSAGETYPE.USERINFOUPDATED, {userInfo:this.chatDisplay.userInfos[this.id]});
	} else {
		this.chatDisplay.loadInitialUserInfo(this.id, this.name, this.firstName);
	}
	if (this.chatDisplay.histories[this.id]) {
		this._setHistory(this.chatDisplay.histories[this.id]);
	}
}, _onHistoryInitialHandler:function (requestID, response) {
	if (requestID != this.historyRequestID) {
		presence.debug("tabs: got old history async, ignoring");
		return false;
	}
}, _onHistoryResponse:function (fromRestart, response) {
	var historyPayload = response.getPayload();
	var userInfo = historyPayload.userInfo;
	var logItems = historyPayload.history;
	this.chatDisplay.userInfos[this.id] = userInfo;
	buddyList.updateItemDisplay(this.id);
	this.updateUserInfo();
	this.arbiter.inform(ChatTab.TABMESSAGETYPE.USERINFOUPDATED, {userInfo:userInfo});
	if (historyPayload.fls) {
		buddyList.setFlids(this.id, historyPayload.fls);
	}
	if (historyPayload.overlay) {
		buddyList.addOverlayInfo(historyPayload.overlay);
	}
	if (!logItems) {
		this._showHistoryError();
		return;
	}
	var poppedSendQueue = false;
	if (this.pendingSentMsgs.length > 0 && logItems.length > 0) {
		var pendingSendMsg = this.pendingSentMsgs[0];
		for (var i = logItems.length - 1; i >= 0; i--) {
			var logItem = logItems[i];
			if (logItem.to == this.id) {
				var timeDiff = Math.abs(pendingSendMsg.time - logItem.time);
				if (timeDiff < this.pendingToLogCompareWindow && pendingSendMsg.text == logItem.msg.text) {
					this._setMsgInfoMarkup(pendingSendMsg.msgID, "");
					this.pendingSentMsgs.shift();
					this._popSendQueue();
					this.poppedSendQueue = true;
					break;
				}
			}
		}
		var lastLogTime = logItems[logItems.length - 1].time;
		for (var i = 0; i < this.pendingSentMsgs.length; i++) {
			var pendingSendMsg = this.pendingSentMsgs[i];
			if (pendingSendMsg.time < lastLogTime) {
				pendingSendMsg.time = (++lastLogTime);
			}
		}
	}
	var pendingLogMsgs = this.chatDisplay.histories[this.id];
	if (pendingLogMsgs) {
		if (logItems.length > 0) {
			var lastLogItem = logItems[logItems.length - 1];
			var lastTime = lastLogItem.time;
			for (var i = 0; i < pendingLogMsgs.length; i++) {
				var logItem = pendingLogMsgs[i];
				if (logItem.time > lastTime) {
					logItems.push(logItem);
				}
			}
		} else {
			logItems = pendingLogMsgs;
		}
	}
	this._setHistory(logItems);
	this.chatDisplay.histories[this.id] = logItems;
	if (fromRestart) {
		if (!poppedSendQueue) {
			this._popSendQueue();
		}
	}
}, _onHistoryError:function (response) {
	this._showHistoryError();
}, _showHistoryError:function () {
	show(this.chatHistoryError);
	this.scrollToBottom();
}, getHistory:function (fromRestart) {
	var requestID = ++(this.historyRequestID);
	new AsyncRequest().setInitialHandler(this._onHistoryInitialHandler.bind(this, requestID)).setHandler(this._onHistoryResponse.bind(this, fromRestart)).setErrorHandler(this._onHistoryError.bind(this)).setTransportErrorHandler(this._onHistoryError.bind(this)).setMethod("GET").setReadOnly(true).setOption("suppressErrorAlerts", true).setData({"id":this.id}).setURI("/ajax/chat/history.php").disableNectar().send();
}, _setHistory:function (logItems) {
	this.lastLogItem = null;
	var markup = "";
	var sentIndex = 0;
	var sentMsgs = [];
	Array.prototype.push.apply(sentMsgs, this.failedSentMsgs);
	Array.prototype.push.apply(sentMsgs, this.pendingSentMsgs);
	var prevItemTime = 0;
	var anyVisibleMessages = false;
	this.arbiter.inform(ChatTab.TABMESSAGETYPE.HISTORYBEGIN);
	for (var i = 0; i < logItems.length; i++) {
		var logItem = logItems[i];
		if (typeof this.messageTypes[logItem.type] == undefined) {
			continue;
		}
		if (!this.messageTypes[logItem.type].preserveHistory) {
			anyVisibleMessages = true;
		}
		for (; sentIndex < sentMsgs.length; sentIndex++) {
			var sentMsg = sentMsgs[sentIndex];
			if (!this.messageTypes[sentMsg.type].preserveHistory) {
				anyVisibleMessages = true;
			}
			if (sentMsg.time > prevItemTime && sentMsg.time <= logItem.time) {
				if (this.messageTypes[sentMsg.type].visible) {
					markup += this._renderMsg(presence.user, this.id, sentMsg.time, sentMsg, sentMsg.msgID, sentMsg.isError, sentMsg.infoMarkup);
				}
			} else {
				break;
			}
		}
		if (logItem.type == "msg") {
			markup += this._renderMsg(logItem.from, logItem.to, logItem.time, logItem.msg);
		} else {
			if (logItem.type == "mf" && chatOptions.getSetting("minifeed")) {
				markup += this._renderMinifeed(logItem.from, logItem.time, logItem.markup);
			} else {
				if (logItem.type == "online") {
					markup += this._renderVisibilityChange(logItem.time, logItem.text);
				} else {
					this.arbiter.inform(ChatTab.TABMESSAGETYPE.HISTORYITER, {item:logItem});
				}
			}
		}
		this.lastLogItem = logItem;
		prevItemTime = logItem.time;
	}
	this.arbiter.inform(ChatTab.TABMESSAGETYPE.HISTORYEND);
	for (; sentIndex < sentMsgs.length; sentIndex++) {
		var sentMsg = sentMsgs[sentIndex];
		if (this.messageTypes[sentMsg.type].visible) {
			markup += this._renderMsg(presence.user, this.id, sentMsg.time, sentMsg, sentMsg.msgID, sentMsg.isError, sentMsg.infoMarkup);
			this.lastLogItem = {"type":sentMsg.type, "from":presence.user, "to":this.id, "time":sentMsg.time, "msg":sentMsg};
		}
	}
	hide(this.chatHistoryError);
	this.chatConvContent.innerHTML = markup;
	this.scrollToBottom();
	if (this.actions.setVisible("clearHistory", anyVisibleMessages).refresh()) {
		this.handleResize();
	}
	this.historyLoaded = true;
}, _onClearHistoryError:function (response) {
	var chat = _tx("Chat");
	presence.showAsyncError(response, _tx("Couldn't clear {Chat} history", {"Chat":chat}));
	CSS.removeClass(this.tabHandle, "history_clearing");
}, _onClearHistoryResponse:function (response) {
	CSS.removeClass(this.tabHandle, "history_clearing");
	var newHistory = [];
	for (var i = 0; i < this.chatDisplay.histories[this.id].length; i++) {
		var item = this.chatDisplay.histories[this.id][i];
		if (typeof this.messageTypes[item.type] == "undefined" || this.messageTypes[item.type].preserveHistory) {
			newHistory.push(item);
		}
	}
	this._setHistory(this.chatDisplay.histories[this.id] = newHistory);
}, clearHistory:function () {
	CSS.addClass(this.tabHandle, "history_clearing");
	new AsyncRequest().setHandler(this._onClearHistoryResponse.bind(this)).setErrorHandler(this._onClearHistoryError.bind(this)).setTransportErrorHandler(this._onClearHistoryError.bind(this)).setData({"clear_history_id":this.id}).setURI(this.chatDisplay.settingsURL).send();
}, _isCurrentPendingSend:function (msgID) {
	return (this.pendingSentMsgs.length > 0 && msgID == this.pendingSentMsgs[0].msgID);
}, _onSendInitialHandler:function (response) {
	this.lastMessageHadOfflineResponse = false;
}, _onSendResponse:function (msgID, response) {
	var payload = response.getPayload();
	if (this._isCurrentPendingSend(msgID)) {
		var pendingMsg = this.pendingSentMsgs[0];
		pendingMsg.asyncSuccess = true;
	}
	if (payload && payload.warning) {
		var warningMarkup = this._renderMsgWarningMarkup(payload.warning.title + "<br />" + payload.warning.body);
		this._setMsgInfoMarkup(msgID, warningMarkup, "msg_warning");
	}
}, _onSendTransportError:function (msgID, response) {
	if (!this._isCurrentPendingSend(msgID)) {
		return;
	}
}, _onSendError:function (msgID, response) {
	if (!this._isCurrentPendingSend(msgID)) {
		return;
	}
	var payload = response.getPayload();
	var error = response.getError();
	var desc = presence.getErrorDescription(response);
	if (error == kError_Chat_SendOtherNotAvailable) {
		this.lastMessageHadOfflineResponse = true;
		buddyList.setUnavailable(this.id);
		for (var i = 0; i < this.pendingSentMsgs.length; i++) {
			var msg = this.pendingSentMsgs[i];
			var msgElement = ge("msg_" + this.id + "_" + msg.msgID);
			if (msgElement) {
				var func_name = window.MessageComposer ? "MessageComposer.createAndShowNew" : "message_dialog.show";
				var onclick = sprintf("onclick=\"" + func_name + "(%d,%e,%e); return false;\"", this.id, "", msg.text);
				var link = presence.renderLink(this.chatDisplay.messageURL + "&id=" + this.id, _tx("send as a message"), onclick);
				var markup = _tx("{message} ({=send as a message})", {"message":this._renderMsgHtmlize(msg), "=send as a message":link});
				set_inner_html(msgElement, markup);
			}
		}
	} else {
		if (error == kError_Chat_NotAvailable) {
			this.lastMessageHadOfflineResponse = true;
			chatOptions.setVisibility(false);
			presence.doSync();
		} else {
			if (error == kError_Chat_TooManyMessages) {
				desc = payload.error.title;
				new ErrorDialog().showError(payload.error.title, payload.error.body);
			} else {
				new ErrorDialog().showError(response.errorSummary, response.errorDescription);
			}
		}
	}
	this._sendErrorAll(desc);
}, _renderMsgWarningMarkup:function (desc) {
	return "<p class=\"chat_notice chat_msg_warning\">" + desc + "</p>";
}, _renderMsgErrorMarkup:function (desc) {
	return "<p class=\"chat_notice chat_msg_not_sent\">" + desc + "</p>";
}, _sendErrorAll:function (desc) {
	var errorMarkup = this._renderMsgErrorMarkup(desc);
	var isFirst = true;
	while (this.pendingSentMsgs.length) {
		var pendingMsg = this.pendingSentMsgs.shift();
		pendingMsg.isError = true;
		if (isFirst) {
			pendingMsg.infoMarkup = errorMarkup;
		}
		this._setMsgInfoMarkup(pendingMsg.msgID, errorMarkup, "msg_error");
		this.failedSentMsgs.push(pendingMsg);
		isFirst = false;
		errorMarkup = "";
	}
}, _sendError:function (msgID, desc) {
	var errorMarkup = this._renderMsgErrorMarkup(desc);
	var pendingMsg = this.pendingSentMsgs.shift();
	pendingMsg.isError = true;
	pendingMsg.infoMarkup = errorMarkup;
	this._setMsgInfoMarkup(msgID, errorMarkup, "msg_error");
	this.failedSentMsgs.push(pendingMsg);
	this._popSendQueue();
	this._bumpSendingMessageDisplay(msgID);
}, _createMessage:function (text, type) {
	var msgID = rand32() + 1;
	var time = presence.getTime();
	if (this.lastLogItem && time < this.lastLogItem.time) {
		time = this.lastLogItem.time + 1;
	}
	var pendingMsg = {text:text, msgID:msgID, type:type, time:time, asyncSuccess:false, isError:false, errorMarkup:""};
	return pendingMsg;
}, _flushSmallQueue:function () {
	if (this.pendingSentMsgs.length == 1) {
		this._sendMessage(this.pendingSentMsgs[0], !channelManager.iframeEverLoaded);
	}
}, _updateChatActivity:function (pendingMsg, msg) {
	this.lastLogItem = {"type":pendingMsg.type, "from":presence.user, "to":this.id, "time":pendingMsg.time, "msg":msg};
	this.chatDisplay.chatActivityTime = (new Date()).getTime();
	presence.doSync();
}, sendInput:function () {
	var text = this.chatInput.value;
	if (!text || !text.match(/[^\s]/)) {
		return;
	}
	if (this.actions.setVisible("clearHistory", true).refresh()) {
		this.handleResize();
	}
	this.chatInput.value = "";
	var pendingMsg = this._createMessage(text, "msg");
	this.pendingSentMsgs.push(pendingMsg);
	this._flushSmallQueue();
	var msg = {"text":text};
	var sendMarkup = this._renderMsg(presence.user, this.id, pendingMsg.time, msg, pendingMsg.msgID);
	this._addConvMarkup(sendMarkup);
	this._updateChatActivity(pendingMsg, msg);
	this._resetTypingState();
}, notifyTypingState:function (state) {
	if (state != this.typingRemoteState) {
		this.typingRemoteState = state;
		presence.debug("tabs: notifyTyping(" + state + ")");
		if (!channelManager.iframeEverLoaded) {
			return;
		}
		var data = {"typ":state, "to":this.id};
		new AsyncRequest().setHandler(bagofholding).setErrorHandler(bagofholding).setTransportErrorHandler(bagofholding).setData(data).setURI("/ajax/chat/typ.php").disableNectar().send();
	}
}, _sendMessage:function (pendingMsg, timeoutOnly) {
	pendingMsg.time = presence.getTime();
	if (this.lastLogItem && pendingMsg.time < this.lastLogItem.time) {
		pendingMsg.time = this.lastLogItem.time + 1;
	}
	clearTimeout(this.sendingDisplayTimeout);
	clearTimeout(this.checkMessageSentTimeout);
	this.sendingDisplayTimeout = setTimeout(this._checkMessageSentShort.bind(this, pendingMsg.msgID), this.sendingDisplayDelay);
	this.checkMessageSentTimeout = setTimeout(this._checkMessageSentLong.bind(this, pendingMsg.msgID), this.sendingCheckDelay);
	if (timeoutOnly) {
		return;
	}
	var msgID = pendingMsg.msgID;
	var history = this.chatDisplay.histories[this.id];
	var lastConvoTime = null;
	if (history) {
		for (var i = history.length - 1; i > 0; i--) {
			if (history[i].type == "msg") {
				lastConvoTime = history[i].time;
				break;
			}
		}
	}
	var sendData = {msg_id:msgID, client_time:pendingMsg.time, to:this.id, num_tabs:this.chatDisplay.numTabs, pvs_time:lastConvoTime};
	if (pendingMsg.type == "video") {
		sendData.action = pendingMsg.action;
		sendData.uid = pendingMsg.uid;
		var endpoint = "/ajax/chat/video.php";
	} else {
		sendData.msg_text = pendingMsg.text;
		var endpoint = "/ajax/chat/send.php";
	}
	new AsyncRequest().setInitialHandler(this._onSendInitialHandler.bind(this)).setHandler(this._onSendResponse.bind(this, msgID)).setErrorHandler(this._onSendError.bind(this, msgID)).setTransportErrorHandler(this._onSendTransportError.bind(this, msgID)).setData(sendData).setURI(endpoint).send();
}, _popSendQueue:function () {
	if (this.pendingSentMsgs.length == 0) {
		return;
	}
	var pendingMsg = this.pendingSentMsgs[0];
	this._sendMessage(pendingMsg);
}, _checkMessageSentShort:function (msgID) {
	if (this._isCurrentPendingSend(msgID)) {
		this._setSendingDisplay(this.pendingSentMsgs[0]);
	}
}, _checkMessageSentLong:function (msgID) {
	if (this._isCurrentPendingSend(msgID)) {
		if (this.pendingSentMsgs[0].asyncSuccess) {
			this._sendErrorAll(_tx("Could not connect to Facebook {Chat} at this time.", {"Chat":_tx("Chat")}));
		} else {
			if (channelManager.iframeIsLoaded) {
				presence.error("tabs: send took too long; resending and invalidating old one");
				this._sendMessage(this.pendingSentMsgs[0]);
			} else {
				presence.error("tabs: send took too long, but iframe isn't yet loaded.  will check again later.");
				setTimeout(this._checkMessageSentLong.bind(this, this.pendingSentMsgs[0].msgID), this.sendingCheckDelay);
			}
		}
	}
}, _bumpSendingMessageDisplay:function (msgID) {
	if (msgID == this.sendingDisplayMsgID) {
		this._setMsgInfoMarkup(msgID, "");
		if (this.pendingSentMsgs.length > 0) {
			this._setSendingDisplay(this.pendingSentMsgs[0]);
		}
	}
}, _setSendingDisplay:function (pendingMsg) {
	this.sendingDisplayMsgID = pendingMsg.msgID;
	pendingMsg.infoMarkup = "<p class=\"chat_notice sending\">" + _tx("Sending:") + "</p>";
	this._setMsgInfoMarkup(pendingMsg.msgID, pendingMsg.infoMarkup);
}, _setMsgInfoMarkup:function (pendingMsgID, markup, msgClass) {
	var msgElement = ge("msg_" + this.id + "_" + pendingMsgID);
	if (!msgElement) {
		return;
	}
	var infoElement = ge("pending_" + this.id + "_" + pendingMsgID);
	if (infoElement) {
		infoElement.innerHTML = markup;
	}
	if (msgClass) {
		CSS.addClass(msgElement, msgClass);
	}
	this.scrollToBottom();
}, updateUserInfo:function () {
	var userInfo = this.chatDisplay.userInfos[this.id];
	this.chatInfoPic.src = userInfo.thumbSrc;
	var statusMarkup = "&nbsp;";
	var dateMarkup = "&nbsp;";
	var status;
	if (status = userInfo.status) {
		var time = userInfo.statusTimeRel;
		statusMarkup = html_hyperlink(status, this.statusTextProcessor, this.statusTextProcessor);
		dateMarkup = time;
	}
	if (ua.ie() < 7) {
		dateMarkup += "<br />&nbsp;<br />&nbsp;";
	}
	this.chatInfoStatusTime.innerHTML = dateMarkup;
	this.chatInfoStatus.innerHTML = statusMarkup;
	CSS.removeClass(this.chatInfo, "hidden");
}, updateStatus:function (status, time, timeRel) {
	var userInfo = this.chatDisplay.userInfos[this.id];
	userInfo.status = status;
	userInfo.statusTime = time;
	userInfo.statusTimeRel = timeRel;
	this.chatDisplay.userInfos[this.id] = userInfo;
	buddyList.updateItemDisplay(this.id);
	this.updateUserInfo();
}, tabHitAreaOnClick:function () {
	if (this.suppressHeaderCollapse) {
		return;
	}
	if (!presence.inVideoChat) {
		if (presence.inPopoutWindow) {
			this.chatDisplay.focusTab(this.id, this.name, this.firstName);
		} else {
			this.chatDisplay.toggleTab(this.id, this.name, this.firstName);
		}
	}
	this.chatDisplay.doStopBlinking();
}, showChat:function () {
	if (presence.inVideoChat) {
		animation(this.chatWrapper).to("right", "0").duration(300).ease(animation.ease.end).go();
		animation(this.videochat_swf).to("marginRight", "226px").duration(300).ease(animation.ease.end).go();
		this.arbiter.inform(ChatTab.TABMESSAGETYPE.DISPLAYCHAT, {visible:true});
	}
}, tabXOnClick:function (e) {
	if (presence.inVideoChat) {
		animation(this.chatWrapper).to("right", "-228px").duration(300).ease(animation.ease.end).go();
		animation(this.videochat_swf).to("marginRight", "0").duration(300).ease(animation.ease.end).go();
		this.arbiter.inform(ChatTab.TABMESSAGETYPE.DISPLAYCHAT, {visible:false});
	} else {
		this.chatDisplay.closeTab(this.id);
		this.chatDisplay.doStopBlinking();
	}
	$E(e).kill();
}, headerLinkMouseOver:function () {
	CSS.addClass(this.chatHeader, "suppress_hover");
	this.suppressHeaderCollapse = true;
}, headerLinkMouseOut:function () {
	CSS.removeClass(this.chatHeader, "suppress_hover");
	this.suppressHeaderCollapse = false;
}, chatConvOnMouseDown:function (event) {
	event = $E(event);
	if (event.button != 0) {
		return;
	}
	this.chatDisplay.doStopBlinking();
}, chatConvOnMouseUp:function () {
	if (DOM.getSelectionSupported() && (DOM.getSelection() == "")) {
		this.focusChatInput();
	}
}, focusChatInput:function () {
	if (!this.tabDisabled && this.isTabVisible()) {
		this.chatInput.focus();
	}
}, _buildUI:function () {
	var tabCountID = "missed_count_" + this.id;
	var chatWindowWrapperID = "chat_window_wrapper_" + this.id;
	var chatConvID = "chat_conv_" + this.id;
	var chatHistoryErrorID = "chat_history_error_" + this.id;
	var chatHeaderID = "chat_header_" + this.id;
	var chatInfoID = "chat_info_" + this.id;
	var chatInfoLinkID = "chat_info_link_" + this.id;
	var chatInfoPicID = "chat_info_pic_" + this.id;
	var chatInfoStatusID = "chat_info_status_" + this.id;
	var chatInfoStatusTimeID = "chat_info_status_time_" + this.id;
	var chatConvContentID = "chat_conv_content_" + this.id;
	var chatInputID = "chat_input_" + this.id;
	var chatShadowInputID = "chat_shadow_input_" + this.id;
	var chatToolboxID = "chat_toolbox_" + this.id;
	var chat = _tx("Chat");
	var handleName = htmlize(this.trimName(this.maxHandleLen));
	var titleName = htmlize(this.trimName(this.maxTitleLen));
	var chatHeaderLinkMouseOver = " onmouseover=\"" + this.tabRef + ".headerLinkMouseOver();\" onmouseout=\"" + this.tabRef + ".headerLinkMouseOut();\"";
	var profileURL = this.chatDisplay.profileURL + "?id=" + this.id;
	var chatHeaderPicLink = presence.renderLink(profileURL, "<img class=\"chat_info_pic\" id=\"" + chatInfoPicID + "\" title=\"View Profile\" style=\"display:block;\">", chatHeaderLinkMouseOver);
	var chatHeaderNameLink = presence.renderLink(profileURL, titleName, chatHeaderLinkMouseOver);
	var tabMarkupArr = ["<div class=\"tab_button_div\" ", "onmouseover=\"CSS.addClass(this, 'hover')\" ", "onmouseout=\"CSS.removeClass(this, 'hover')\">", "<div title=\"", _tx("Show/Hide {Chat} Window", {"Chat":chat}), "\" ", "class=\"tab_hit_area\" ", "onclick=\"", this.tabRef, ".tabHitAreaOnClick()\">", "<div class=\"tab_name\">", "<span>", handleName, "</span>", "<img src=\"http://static.ak.fbcdn.net/images/spacer.gif\"", "class=\"tab_availability\" alt=\"", handleName, "\"/>", "</div>", "</div>", "<div class=\"tab_count\" id=\"", tabCountID, "\"></div>", "<div title=\"", _tx("Close {Chat} Window", {"Chat":chat}), "\" ", "class=\"tab_x\" ", "onclick=\"", this.tabRef, ".tabXOnClick(event)\" ", "onmouseover=\"CSS.addClass(this, 'hover')\" ", "onmouseout=\"CSS.removeClass(this, 'hover')\">", "</div>", "<div class=\"chat_window_wrapper\" id=\"", chatWindowWrapperID, "\">", "<div class=\"chat_window\">", "<div class=\"chat_header\" id=\"", chatHeaderID, "\" onclick=\"", this.tabRef, ".tabHitAreaOnClick()\">", "<div class=\"header_buttons\">", "<a title=\"", _tx("Close {Chat} Window", {"Chat":chat}), "\" ", "class=\"close\" ", "onmouseover=\"CSS.addClass($('", chatHeaderID, "'), 'suppress_hover')\" ", "onmouseout=\"CSS.removeClass($('", chatHeaderID, "'), 'suppress_hover')\" ", "onclick=\"", this.tabRef, ".tabXOnClick(event)\">", "</a>", "<a title=\"", _tx("Hide {Chat} Window", {"Chat":chat}), "\" ", "class=\"minimize\">", "</a>", "</div>", chatHeaderPicLink, "<div class=\"chat_header_name\">", chatHeaderNameLink, "</div>", "</div>", "<div class=\"chat_info\" id=\"", chatInfoID, "\">", "<span class=\"chat_info_status\" id=\"", chatInfoStatusID, "\"></span><wbr />", "<span class=\"chat_info_status_time\" id=\"", chatInfoStatusTimeID, "\"></span>", "</div>", "<div id=\"", chatToolboxID, "\" class=\"toolbox\">", "<div class=\"chat_actions\"></div>", "</div>", "<div class=\"chat_conv\" id=\"", chatConvID, "\" ", "onmousedown=\"", this.tabRef, ".chatConvOnMouseDown(event)\" ", "onmouseup=\"", this.tabRef, ".chatConvOnMouseUp()\">", "<div class=\"chat_notice\" id=\"", chatHistoryErrorID, "\" style=\"display:none\">", _tx("Couldn't retrieve chat history"), "</div>", "<div class=\"chat_conv_content\" id=\"", chatConvContentID, "\"></div>", "</div>", "<div class=\"chat_input_div\">", "<textarea class=\"chat_shadow_input\" id=\"", chatShadowInputID, "\"></textarea>", "<div class=\"chat_input_icon\"></div>", "<textarea class=\"chat_input\" id=\"", chatInputID, "\" ", "onclick=\"chatDisplay.doStopBlinking()\" ", "onkeydown=\"return ", this.tabRef, ".inputKeyDown(event)\" ", "onkeypress=\"return ", this.tabRef, ".inputKeyPress(event)\" ", "></textarea>", "<div class=\"chat_input_border\"></div>", "</div>", "</div>", "</div>", "</div>"];
	this.tabHandle = document.createElement("div");
	var chatTabBar = ge("chat_tab_bar");
	var otherTab = null;
	for (var id in this.chatDisplay.tabs) {
		otherTab = this.chatDisplay.tabs[id];
	}
	if (otherTab) {
		chatTabBar.insertBefore(this.tabHandle, otherTab.tabHandle);
	} else {
		chatTabBar.appendChild(this.tabHandle);
	}
	this.tabHandle.id = "tab_handle_" + this.id;
	CSS.setClass(this.tabHandle, "tab_handle");
	this.tabHandle.style.width = this.handleWidth + "px";
	this.tabHandle.style.display = "block";
	this.tabHandle.innerHTML = tabMarkupArr.join("");
	this.tabCount = ge(tabCountID);
	this.chatWrapper = ge(chatWindowWrapperID);
	this.chatConv = ge(chatConvID);
	this.chatHistoryError = ge(chatHistoryErrorID);
	this.chatHeader = ge(chatHeaderID);
	this.chatInfo = ge(chatInfoID);
	this.chatInfoLink = ge(chatInfoLinkID);
	this.chatInfoPic = ge(chatInfoPicID);
	this.chatInfoStatus = ge(chatInfoStatusID);
	this.chatInfoStatusTime = ge(chatInfoStatusTimeID);
	this.chatConvContent = ge(chatConvContentID);
	this.chatInput = ge(chatInputID);
	this.chatShadowInput = ge(chatShadowInputID);
	this.toolbox = ge(chatToolboxID);
	var chatActions = DOM.find(this.toolbox, "div.chat_actions");
	this.actions = new ChatTabActions(chatActions);
	this.actions.appendAction("clearHistory", _tx("Clear {Chat} History", {"Chat":chat}), this.clearHistory.bind(this));
	this.arbiter.inform(ChatTab.TABMESSAGETYPE.CREATETAB, {toolbox:this.toolbox});
	this.actions.refresh();
	this.popoutChatTabs = presence.inPopoutWindow ? ge("open_chats") : null;
	this.adjustWrapperBottom();
	this._updateNumMissedDisplay();
}, adjustWrapperBottom:function () {
	if (presence.isFF2) {
		var holderBottom = presence.getHolderBottomPosition();
		CSS.setStyle(this.chatWrapper, "bottom", (holderBottom + 24) + "px");
	}
}, show:function () {
	this.tabHandle.style.display = "block";
}, hide:function () {
	this.tabHandle.style.display = "none";
}, inputKeyDown:function (event) {
	event = $E(event);
	this.chatDisplay.doStopBlinking();
	if (this.chatDisplay.gatedFeatures.typ_send) {
		this._updateTyping.bind(this).defer();
	}
	if (event.keyCode == KEYS.RETURN && !event.shiftKey) {
		if (this.chatInput.value) {
			this.sendInput();
		}
	}
	if (event.keyCode == KEYS.DELETE || event.keyCode == KEYS.BACKSPACE) {
		this.handleResize.bind(this).defer();
	}
}, inputKeyPress:function (event) {
	event = $E(event);
	this.handleResize.bind(this).defer();
	if (event.keyCode == KEYS.RETURN && !event.shiftKey) {
		event.returnValue = false;
		return false;
	}
}, _updateTyping:function () {
	var state = this.typingState;
	if (this.chatInput.value.length == 0) {
		if (state == this.INACTIVE) {
		} else {
			this._typingTransition(this.INACTIVE);
		}
	} else {
		if (state == this.TYPING) {
			this._recordKeystroke();
		} else {
			if (state == this.INACTIVE) {
				this._typingTransition(this.TYPING);
				this._recordKeystroke();
			}
		}
	}
}, _recordKeystroke:function () {
	this.typingLastKeystrokeAt = new Date();
	if (!this.typingCheckTimer) {
		this.typingCheckTimer = setTimeout(this._checkTyping.bind(this), this.typingKeystrokeExpiry);
	}
}, _checkTyping:function () {
	var expiresAt = this.typingLastKeystrokeAt.valueOf() + this.typingKeystrokeExpiry;
	var now = new Date().valueOf();
	if (now > expiresAt) {
		this._typingTransition(this.INACTIVE);
	} else {
		clearTimeout(this.typingCheckTimer);
		this.typingCheckTimer = setTimeout(this._checkTyping.bind(this), expiresAt - now + 10);
	}
}, _typingTransition:function (newState) {
	clearTimeout(this.typingCheckTimer);
	this.typingCheckTimer = null;
	typingLastKeystrokeAt = null;
	presence.debug("typing:" + this.typingState + " -> " + newState);
	this.typingState = newState;
	clearTimeout(this.typingNotifyTimer);
	this.typingNotifyTimer = setTimeout(this.notifyTypingState.bind(this, newState), this.typingNotifyDelay);
}, _resetTypingState:function () {
	presence.debug("typing: ** RESET **");
	this.typingState = this.INACTIVE;
	this.typingRemoteState = this.INACTIVE;
	this.typingLastKeystrokeAt = null;
	clearTimeout(this.typingNotifyTimer);
	this.typingNotifyTimer = null;
	clearTimeout(this.typingCheckTimer);
	this.typingCheckTimer = null;
}, trimName:function (maxLength) {
	var name = this.name;
	if (name.length > maxLength) {
		name = name.substring(0, maxLength - 2) + "...";
	}
	return name;
}, handleVisibility:function (onload) {
	var justCameOnline = chatOptions.visibility;
	if (chatOptions.visibility) {
		this._enableTab(true, false, justCameOnline, onload);
	} else {
		this._disableTab(true, justCameOnline, onload);
	}
	this.handleBuddyAvailability(!onload && justCameOnline, onload);
}, handleBuddyAvailability:function (justCameOnline, onload) {
	if (!chatOptions.visibility) {
		return;
	}
	var availability = buddyList.getAvailability(this.id);
	if (availability) {
		this._enableTab(false, availability.i, justCameOnline, onload);
	} else {
		this._disableTab(false, justCameOnline, onload);
	}
}, _enableTab:function (isYou, isIdle, justCameOnline, onload) {
	onload = onload || false;
	var wasDisabled = this.tabDisabled;
	this.tabDisabled = false;
	if (presence.inPopoutWindow) {
		CSS.removeClass(this.popoutTab, "disabled");
		CSS.conditionClass(this.popoutTab, "idle", isIdle);
	}
	CSS.removeClass(this.tabHandle, "disabled");
	CSS.conditionClass(this.tabHandle, "idle", isIdle);
	if ((isYou && !onload) || (!isYou && wasDisabled && !justCameOnline)) {
		this._newVisibilityChange(isYou, onload, true);
	}
}, _disableTab:function (isYou, justCameOnline, onload) {
	onload = onload || false;
	var wasDisabled = this.tabDisabled;
	this.tabDisabled = true;
	if (presence.inPopoutWindow) {
		CSS.addClass(this.popoutTab, "disabled");
		CSS.removeClass(this.popoutTab, "idle");
	}
	CSS.addClass(this.tabHandle, "disabled");
	CSS.removeClass(this.tabHandle, "idle");
	if ((isYou || onload || !wasDisabled) && !this.lastMessageHadOfflineResponse && !justCameOnline) {
		this._newVisibilityChange(isYou, onload, false);
	}
}, handleResize:function () {
	var newConvWidth;
	var newTextWidth;
	var newNotAvailWidth;
	var newTabHeight;
	var heightExtra = 31;
	var toolboxChildren = this.toolbox.childNodes;
	for (var i = 0; i < toolboxChildren.length; ++i) {
		var child = toolboxChildren[i];
		if (shown(child)) {
			heightExtra += Vector2.getElementDimensions(child).y;
		}
	}
	if (presence.inVideoChat || presence.inPopoutWindow) {
		var chatInfoMinHeight = 33;
		heightExtra += Vector2.getElementDimensions(this.chatInfo).y - chatInfoMinHeight;
	}
	if (presence.inVideoChat) {
		var videoChatWidth = 226;
		newConvWidth = videoChatWidth;
		newTextWidth = videoChatWidth - 28;
		newNotAvailWidth = videoChatWidth - 16;
		newTabHeight = presence.popoutHeight - 77;
	} else {
		if (presence.inPopoutWindow) {
			var popoutWidth = (presence.popoutWidth > 330) ? presence.popoutWidth : 330;
			newConvWidth = popoutWidth - this.flPopoutWidthOffset;
			newTextWidth = popoutWidth - this.flPopoutWidthOffset - 28;
			newNotAvailWidth = popoutWidth - this.flPopoutWidthOffset - 16;
			newTabHeight = presence.popoutHeight - 77;
		} else {
			newConvWidth = this.popinWidth;
			newTextWidth = this.popinWidth - 28;
			newNotAvailWidth = this.popinWidth - 8;
			newTabHeight = this.popinHeight;
		}
	}
	this.chatShadowInput.style.width = this.chatInput.style.width = newTextWidth + "px";
	this.chatShadowInput.value = this.chatInput.value;
	var textHeight = this.chatShadowInput.scrollHeight;
	if (!textHeight || presence.isOpera) {
		textHeight = this.minTextHeight;
		if (this.chatInput.value) {
			var re = new RegExp("([\n]|[^\n]{" + parseInt(newTextWidth / 8) + "})", "g");
			var matches = this.chatInput.value.match(re);
			if (matches) {
				textHeight = (matches.length + 1) * this.minTextHeight;
			}
		}
		if (presence.isSafari2) {
			textHeight += 8;
		}
	}
	if (ua.ie()) {
		textHeight -= 6;
	}
	if (textHeight > this.maxTextHeight) {
		textHeight = this.maxTextHeight;
	} else {
		if (textHeight < this.minTextHeight) {
			textHeight = this.minTextHeight;
		}
	}
	if (presence.isSafari2) {
		heightExtra -= 7;
	} else {
		if (ua.ie() < 7) {
			if (this.tabDisabled) {
				heightExtra += 4;
			}
		}
	}
	if (!presence.inPopoutWindow || ua.ie()) {
		this.chatHeader.style.width = newConvWidth + "px";
		this.chatInfo.style.width = (newConvWidth - 55) + "px";
		this.chatConv.style.width = newConvWidth + "px";
	}
	this.chatInput.style.height = textHeight + "px";
	this.chatConv.style.height = Math.max(0, (newTabHeight - heightExtra - textHeight)) + "px";
	this.scrollToBottom();
}, isUserScrolled:function () {
	return (this.chatConv.scrollHeight > this.chatConv.scrollTop + this.chatConv.clientHeight);
}, scrollToBottom:function () {
	this.chatConv.scrollTop = this.chatConv.scrollHeight;
}, unfocus:function () {
	this.focused = false;
	CSS.removeClass(this.tabHandle, "focused");
}, focus:function (hidden, chatDisplayLoaded) {
	if (this.focused) {
		return;
	}
	CSS.addClass(this.tabHandle, "focused");
	this.focused = true;
	if (!hidden) {
		this._onFocusUIActions();
		setTimeout(this._onFocusUIActions.bind(this), 100);
	}
	if (!this.historyLoaded) {
		this.getHistory(false);
	}
	this._updateNumMissed(0);
	this._stopBounce();
}, _onFocusUIActions:function () {
	this.handleResize();
	if (ua.ie() < 7 && presence.inPopoutWindow) {
		this.chatWrapper.style.top = "67px";
	}
	this.focusChatInput();
}, _startBounce:function () {
	if (presence.isFF2 && presence.isWindows) {
		return;
	}
	this.bounceAnimation = animation(this.tabCount).to("top", -11).duration(this.bounceDuration + 40).checkpoint().to("top", this.bounceOrgPosition).duration(this.bounceDuration).checkpoint().to("top", -11).duration(this.bounceDuration + 40).checkpoint().to("top", this.bounceOrgPosition).duration(this.bounceDuration).checkpoint().to("top", -7).duration(this.bounceDuration).checkpoint().to("top", -5).duration(this.bounceDuration).checkpoint().to("top", this.bounceOrgPosition).duration(this.bounceDuration).checkpoint().go();
}, _stopBounce:function () {
	if (this.bounceAnimation) {
		this.bounceAnimation.stop();
		this.bounceAnimation = null;
	}
}, close:function () {
	this.tabHandle.parentNode.removeChild(this.tabHandle);
	this.closePopoutChat();
}, addPopoutChat:function (id) {
	if (this.popoutChatTabs) {
		this.popoutTab = document.createElement("div");
		this.popoutTab.id = "popout_tab_" + this.id;
		this.popoutTab.className = "popout_tab_button" + (this.chatDisplay.gatedFeatures.typ_show ? "" : " vanilla") + ((id == this.chatDisplay.focused) ? " highlight" : "");
		var popoutCountID = "popout_missed_" + this.id;
		this.popoutTab.innerHTML = "<div class=\"popout_tab_hit_area\" onclick=\"" + this.tabRef + ".tabHitAreaOnClick();\">" + "<div id=\"" + popoutCountID + "\" class=\"popout_tab_count\"></div>" + "<div class=\"popout_tab_name\">" + this.name + "</div>" + "</div>" + "<div class=\"popout_tab_x\" onclick=\"" + this.tabRef + ".tabXOnClick(event)\"></div>";
		this.popoutChatTabs.appendChild(this.popoutTab);
		this.popoutTabCount = ge(popoutCountID);
		show(ge("popout_chat_tabs"));
	}
}, closePopoutChat:function () {
	if (this.popoutChatTabs) {
		this.popoutChatTabs.removeChild(this.popoutTab);
		if (this.popoutChatTabs.childNodes.length <= 0) {
			hide(ge("popout_chat_tabs"));
		}
	}
}, selectPopoutChat:function () {
	CSS.addClass(this.popoutTab, "highlight");
}, deselectPopoutChat:function () {
	CSS.removeClass(this.popoutTab, "highlight");
}, _newVisibilityChange:function (isYou, onload, online) {
	var time = presence.getTime();
	var text;
	if (isYou) {
		if (online) {
			text = _tx("You are online.", {"name":this.firstName});
		} else {
			text = _tx("You are not online.", {"name":this.firstName});
		}
	} else {
		if (online) {
			text = _tx("{name} is online.", {"name":this.firstName});
		} else {
			text = _tx("{name} is offline.", {"name":this.firstName});
		}
	}
	var item = {"type":"online", "time":time, "text":text};
	var history = this.chatDisplay.getHistory(this.id, true);
	history.push(item);
	var markup = this._renderVisibilityChange(time, text);
	this._addConvMarkup(markup);
	this.lastLogItem = item;
}, newMinifeed:function (mfItem) {
	if (mfItem.status) {
		this.updateStatus(mfItem.status, mfItem.time, mfItem.statusTimeRel);
	}
	if (this.lastLogItem && this.lastLogItem.type == "mf") {
		return;
	}
	var history = this.chatDisplay.getHistory(this.id, true);
	history.push(mfItem);
	var markup = this._renderMinifeed(mfItem.from, mfItem.time, mfItem.markup);
	this._addConvMarkup(markup);
	this.lastLogItem = mfItem;
}, newTyping:function (msgItem) {
	var from = msgItem.from;
	var to = msgItem.to;
	var typ = msgItem.st;
	if (!this.chatDisplay.gatedFeatures.typ_show) {
		return;
	} else {
		if ((new Date() - this.lastMessageAt) < this.typingNotifyDelay) {
			return;
		}
	}
	presence.debug("typing from " + from + ": " + typ);
	var show_typing = (typ == this.TYPING) && (this.numMissed == 0);
	if (to != this.id) {
		if (presence.inPopoutWindow) {
			CSS.conditionClass(this.popoutTab, "typing", show_typing);
		}
		CSS.conditionClass(this.tabHandle, "typing", show_typing);
		buddyList.setAvailable(this.id);
	}
}, _isChatMessage:function (message) {
	return;
	typeof this.messageTypes[message.type] != "undefined" && this.messageTypes[message.type].user;
}, newMsg:function (msgItem) {
	var from = msgItem.from;
	var to = msgItem.to;
	var type = msgItem.type;
	var msg = msgItem.msg;
	var time = msg.time;
	var clientTime = msg.clientTime;
	var msgID = msg.msgID;
	var shouldRender = true;
	this.lastMessageAt = new Date();
	var history = this.chatDisplay.getHistory(this.id, true);
	var lastMsg = null;
	for (var i = history.length - 1; i >= 0; i--) {
		if (this._isChatMessage(history[i])) {
			lastMsg = history[i];
			break;
		}
	}
	if (lastMsg && time <= lastMsg.time) {
		var found = false;
		for (var i = 0; i < history.length; i++) {
			if (this._isChatMessage(history[i]) && time == history[i].time) {
				found = true;
				break;
			}
		}
		if (found) {
			presence.warn("tabs: already had this msg");
			return;
		}
		for (var i = history.length - 1; i >= 0; i--) {
			var historyItem = history[i];
			if (this._isChatMessage(historyItem) && (historyItem.to != to || (!historyItem.msg.clientTime || historyItem.msg.clientTime < clientTime))) {
				break;
			}
		}
		presence.warn("tabs: merging new msg due to out-of-order server timestamp");
		if (i == history.length - 1) {
			this.chatDisplay.histories[this.id].push(msgItem);
		} else {
			history.splice(i + 1, 0, msgItem);
			this._setHistory(history);
			shouldRender = false;
		}
	} else {
		this.chatDisplay.histories[this.id].push(msgItem);
	}
	if (to != this.id) {
		if (this.chatDisplay.gatedFeatures.sound && this.chatDisplay.isSoundWindow && chatOptions.getSetting("sound")) {
			Sound.play("/sound/pop.mp3");
		}
		buddyList.setAvailable(this.id);
		if (!this.focused) {
			this._updateNumMissed(this.numMissed + 1, time);
			this._startBounce();
		}
		if (presence.inPopoutWindow) {
			CSS.removeClass(this.popoutTab, "typing");
		}
		CSS.removeClass(this.tabHandle, "typing");
	} else {
		this._updateNumMissed(0, time);
		for (var i = 0; i < this.pendingSentMsgs.length; i++) {
			if (msgID == this.pendingSentMsgs[i].msgID) {
				var pendingMsg = this.pendingSentMsgs.splice(i, 1);
				this._bumpSendingMessageDisplay(msgID);
				this._popSendQueue();
				shouldRender = false;
				break;
			}
		}
	}
	this.arbiter.inform(ChatTab.TABMESSAGETYPE.RECEIVEMSG, {type:type, msgItem:msgItem});
	shouldRender = this.messageTypes[type].visible && shouldRender;
	if (shouldRender) {
		var msgMarkup = this._renderMsg(from, to, time, msg);
		this._addConvMarkup(msgMarkup);
		if (this.actions.setVisible("clearHistory", true).refresh()) {
			this.handleResize();
		}
		this.lastLogItem = msgItem;
	}
}, getInputElemId:function () {
	return "chat_input_" + this.id;
}, _updateNumMissed:function (numMissed, time) {
	if (numMissed == this.numMissed || (time && time <= this.missedTime)) {
		return;
	}
	if (numMissed > 99) {
		numMissed = 99;
	}
	this.numMissed = numMissed;
	this.missedTime = time;
	this._updateNumMissedDisplay();
}, _updateNumMissedDisplay:function () {
	this.tabCount.innerHTML = this.numMissed;
	if (this.numMissed > 0) {
		if (this.popoutTabCount) {
			this.popoutTabCount.style.display = "block";
			this.popoutTabCount.innerHTML = this.numMissed;
		} else {
			chatTabSlider.updateMissedCount();
		}
		CSS.addClass(this.tabHandle, "highlight");
		this.tabCount.style.display = "block";
	} else {
		if (this.popoutTabCount) {
			this.popoutTabCount.style.display = "none";
		} else {
			chatTabSlider.updateMissedCount();
		}
		CSS.removeClass(this.tabHandle, "highlight");
		this.tabCount.style.display = "none";
	}
}, _addConvMarkup:function (markup) {
	var isUserScrolled = this.isUserScrolled();
	this.chatConvContent.innerHTML += markup;
	if (!isUserScrolled) {
		this.scrollToBottom();
	}
}, _renderDateBreak:function (time) {
	var newDate = new Date();
	newDate.setTime(time);
	var shouldRender = false;
	var lastDate = new Date();
	if (this.lastLogItem) {
		lastDate.setTime(this.lastLogItem.time);
	}
	if (newDate.getDate() != lastDate.getDate() || newDate.getMonth() != lastDate.getMonth()) {
		shouldRender = true;
	}
	var markup = "";
	if (shouldRender) {
		var classname = "date_divider";
		if (!this.lastLogItem) {
			classname += " first";
		}
		markup = "<div class=\"" + classname + "\">" + renderDate(newDate, !presence.inPopoutWindow) + "</div>";
	}
	return markup;
}, _renderVisibilityChange:function (time, visibilityMarkup) {
	var markup = this._renderDateBreak(time);
	markup += "<div class=\"visibility_change\">" + "<span class=\"time_stamp\">" + chatDisplay.renderServerTime(time) + "</span>" + visibilityMarkup + "</div>";
	return markup;
}, _renderMinifeed:function (from, time, storyMarkup) {
	var markup = this._renderDateBreak(time);
	markup += "<div class=\"minifeed\">" + storyMarkup + "</div>";
	return markup;
}, _renderMsg:function (from, to, time, msg, pendingMsgID, isError, errorMarkup) {
	var fromSelf = from != this.id;
	var fromToSelf = fromSelf && from == to;
	var msgClass = fromSelf && !fromToSelf ? "self" : "other";
	var markup = this._renderDateBreak(time);
	if (!markup && this.lastLogItem && this.lastLogItem.type == "msg" && this.lastLogItem.from == from && time - this.lastLogItem.time < this.msgBunchTime) {
	} else {
		var pic = this.chatDisplay.userInfos[from] && presence.inPopoutWindow ? "<img src=\"" + this.chatDisplay.userInfos[from].thumbSrc + "\" class=\"pic\" />" : "";
		var name = fromSelf ? htmlize(presence.firstName) : presence.renderLink(this.chatDisplay.profileURL + "?id=" + this.id, htmlize(this.firstName));
		var timeStr = chatDisplay.renderServerTime(time);
		markup += "<h5 class=\"" + msgClass + "\">" + pic + " <span class=\"time_stamp ts_" + msgClass + "\">" + timeStr + "</span>" + name + "</h5>";
	}
	if (pendingMsgID || errorMarkup) {
		var pendingElementID = pendingMsgID ? " id=\"pending_" + this.id + "_" + pendingMsgID + "\"" : "";
		markup += "<div" + pendingElementID + " class=\"pic_padding\">" + (errorMarkup ? errorMarkup : "") + "</div>";
	}
	var msgElementID = pendingMsgID ? " id=\"msg_" + this.id + "_" + pendingMsgID + "\"" : "";
	msgClasses = "p_" + msgClass + " pic_padding" + (isError ? " msg_error" : "");
	markup += "<p" + msgElementID + " class=\"" + msgClasses + "\">" + this._renderMsgHtmlize(msg) + "</p>";
	return markup;
}, _renderMsgHtmlize:function (msg) {
	return html_hyperlink(msg.text || "", this._processStyledText.bind(this, this.convTextEmoteProcessor), this.convTextProcessor, true);
}, _processStyledText:function (textProcessor, str) {
	return textProcessor(str).replace(/\b_([^_\*]+)_\b/g, "<u>$1</u>").replace(/(\s|^)\*([^_\*]+)\*(?=$|\s)/g, "$1<b>$2</b>");
}, _processConvText:function (str) {
	return html_wordwrap(str, this.convWrapLimit);
}, _processConvTextEmote:function (str) {
	return Emote.htmlEmote(str, this.convTextProcessor);
}, _processStatusText:function (str) {
	return html_wordwrap(str, this.statusWrapLimit);
}});
function renderDate(date, showRelative) {
	if (showRelative) {
		var today = new Date();
		today.setHours(0);
		today.setMinutes(0);
		today.setSeconds(0);
		today.setMilliseconds(0);
		var dayMilliseconds = 24 * 60 * 60 * 1000;
		var diff = today.getTime() - date.getTime();
		if (diff <= 0) {
			return _tx("Today");
		} else {
			if (diff < dayMilliseconds) {
				return _tx("Yesterday");
			}
		}
	}
	var month = "";
	switch (date.getMonth()) {
	  case 0:
		month = _tx("January");
		break;
	  case 1:
		month = _tx("February");
		break;
	  case 2:
		month = _tx("March");
		break;
	  case 3:
		month = _tx("April");
		break;
	  case 4:
		month = _tx("May");
		break;
	  case 5:
		month = _tx("June");
		break;
	  case 6:
		month = _tx("July");
		break;
	  case 7:
		month = _tx("August");
		break;
	  case 8:
		month = _tx("September");
		break;
	  case 9:
		month = _tx("October");
		break;
	  case 10:
		month = _tx("November");
		break;
	  case 11:
		month = _tx("December");
		break;
	}
	return _tx("{month} {date}", {"month":month, "date":date.getDate()});
}
function ChatDisplay(userInfos, histories, everSentMessage, activeChats, focusedChat, gatedFeatures) {
	this.userInfos = userInfos;
	this.histories = histories;
	this.everSentMessage = everSentMessage;
	this.user = presence.user;
	var wwwBase = env_get("www_base");
	this.profileURL = wwwBase + "profile.php";
	this.messageURL = wwwBase + "inbox/?compose";
	this.settingsURL = "/ajax/chat/settings.php";
	this.gatedFeatures = gatedFeatures;
	this.useUICookieCache = gatedFeatures["ui_cookie_cache"];
	this.renderServerTime = this.gatedFeatures["24h_times"] ? this._renderServerTime24hr : this._renderServerTime12hr;
	this._init(activeChats, focusedChat);
}
ChatDisplay.prototype = {blinkTime:1500, initialBlinkDelay:3000, _init:function (activeChats, focusedChat) {
	this.loaded = false;
	this.tabs = {};
	this.numTabs = 0;
	this.lastFocused = null;
	this.newMsgNames = [];
	this.newMsgNamesIndex = 0;
	this.blinkingTimer = null;
	this.isSoundWindow = false;
	this.windowIsFocused = true;
	this.chatActivityTime = 0;
	if (this.useUICookieCache) {
		this.uiChangeTime = 0;
		this.uiCookieCacheTime = (Env["rep_lag"] + presence.sitevars.CHAT_UI_COOKIE_CACHE_WINDOW) * 1000;
	}
	this.favIcon = null;
	this.altFavIcon = null;
	this.initialFocusedChat = focusedChat;
	this.initialActiveChats = activeChats;
	presence.registerStateStorer(this._store.bind(this));
	presence.registerStateLoader(this._load.bind(this));
	var handledMsgTypes = ["unfocus_chat", "focus_chat", "close_chat", "msg", "video", "mf", "status", "typ"].map(Presence.getArbiterMessageType);
	Arbiter.subscribe(handledMsgTypes, this._handlePresenceMessage.bind(this));
	Arbiter.subscribe(Presence.STARTED, this.start.bind(this));
	Arbiter.subscribe(Presence.SHUTDOWN, this.shutdown.bind(this));
	Arbiter.subscribe(Presence.RESTARTED, this.restart.bind(this));
	Arbiter.subscribe(ChatOptions.VISIBILITY_CHANGED, this.handleVisibility.bind(this));
	Arbiter.subscribe(ChatBuddyList.AVAILABILITY_CHANGED, this._onBuddyAvailability.bind(this));
	Arbiter.subscribe(Presence.WINDOW_RESIZED, this.handleResize.bind(this));
	Event.listen(window, "focus", this.onWindowFocus.bind(this));
	Event.listen(window, "blur", this.onWindowBlur.bind(this));
}, onWindowFocus:function () {
	this.isWindowFocused = true;
	this.doStopBlinking();
}, onWindowBlur:function () {
	this.isWindowFocused = false;
}, start:function () {
	for (var id in this.tabs) {
		this.tabs[id].start();
	}
}, shutdown:function () {
	this._stopBlinking();
}, restart:function () {
	for (var id in this.tabs) {
		this.tabs[id].restart();
	}
}, loadInitialUserInfo:function (id, name, firstName) {
	if (this.userInfos[id]) {
		return;
	}
	this.userInfos[id] = {"name":name, "firstName":firstName, "thumbSrc":"", "status":"", "statusTime":"", "statusTimeRel":""};
}, _loadInitialTabs:function (activeChats, focusedChat) {
	var firstTab = null;
	for (var id in activeChats) {
		if (presence.inVideoChat && id != presence.inVideoChatWith) {
			continue;
		}
		if (!firstTab) {
			firstTab = id;
		}
		if (this.tabs[id]) {
			continue;
		}
		var tabInfo = activeChats[id];
		var name, firstName;
		if (this.userInfos[id]) {
			name = this.userInfos[id].name;
			firstName = this.userInfos[id].firstName;
		} else {
			name = tabInfo.n;
			if (!name) {
				Util.warn("chat display: trying to load chat tab " + id + ", but don't have the name");
				continue;
			}
			firstName = tabInfo.fn ? tabInfo.fn : getFirstName(name);
		}
		var numMissed = tabInfo.m || 0;
		var missedTime = tabInfo.t || 0;
		this.tabs[id] = new ChatTab(this, id, name, firstName, numMissed, missedTime);
		this.numTabs++;
	}
	if (presence.inVideoChat && !firstTab) {
		focusedChat = presence.inVideoChatWith;
	} else {
		if (presence.inPopoutWindow && !focusedChat) {
			focusedChat = firstTab;
		}
	}
	if (focusedChat && (focusedChat != this.focused)) {
		this._focusTab(focusedChat);
	}
}, load:function () {
	this._load(presence.state);
}, _load:function (presenceState) {
	var loadedFromCache = false;
	if (presenceState) {
		this.isSoundWindow = channelManager.isLowestSubdomain();
		if (this.blinkingTimer && presenceState.sb) {
			this._stopBlinking();
		}
		this.chatActivityTime = verifyNumber(presenceState.ct) * 1000;
		if (this.useUICookieCache) {
			var now = presence.getTime();
			this.uiChangeTime = Math.max(this.uiChangeTime, verifyNumber(presenceState.uct) * 1000);
			if (!this.loaded) {
				if (now - this.uiChangeTime < this.uiCookieCacheTime) {
					if (now - presenceState.ut > 60 * 60 * 1000) {
						for (var id in presenceState.t) {
							presenceState.t[id].m = 0;
						}
					}
					presence.debug("chatDisplay: loading tabs from cookie cache");
					this._loadInitialTabs.bind(this, presenceState.t, presenceState.f).defer();
					loadedFromCache = true;
				}
			}
		}
	}
	if (!this.loaded && !loadedFromCache) {
		presence.debug("chatDisplay: loading tabs from server state");
		this._loadInitialTabs.bind(this, this.initialActiveChats, this.initialFocusedChat).defer();
		this.initialFocusedChat = this.initialActiveChats = false;
	}
	this.loaded = true;
}, _store:function (presenceState) {
	presenceState.ct = Math.floor(this.chatActivityTime * 0.001);
	presenceState.sb = (this.blinkingTimer == null) ? 1 : 0;
	if (this.useUICookieCache) {
		presenceState.t = {};
		presenceState.f = null;
		presenceState.uct = 0;
		var now = presence.getTime();
		if (now - this.uiChangeTime < this.uiCookieCacheTime) {
			for (var id in this.tabs) {
				var tab = this.tabs[id];
				presenceState.t[id] = {n:tab.name, m:tab.numMissed};
				if (tab.firstName != getFirstName(tab.name)) {
					presenceState.t[id].fn = tab.firstName;
				}
			}
			presenceState.f = this.focused;
			presenceState.uct = Math.floor(this.uiChangeTime * 0.001);
		}
	}
	return presenceState;
}, handleResize:function () {
	if (!this.focused) {
		return;
	}
	var tab = this.tabs[this.focused];
	tab.handleResize();
}, _sendTabStateChange:function (data) {
	data["window_id"] = presence.windowID;
	new AsyncRequest().setURI(this.settingsURL).setData(data).setHandler(this._onCheckTabStateChangeResponse.bind(this)).setErrorHandler(bagofholding).setTransportErrorHandler(bagofholding).send();
}, _onCheckTabStateChangeResponse:function (response) {
	var payload = response.getPayload();
	if (payload.overlay) {
		buddyList.addOverlayInfo(payload.overlay);
	}
}, reloadTabs:function () {
	for (var id in this.tabs) {
		this.tabs[id].loadData();
	}
}, _closeTab:function (id) {
	if (!this.tabs[id]) {
		return;
	}
	if (this.focused == id) {
		if (presence.inPopoutWindow) {
			var toFocus = null;
			var breakNext = false;
			for (var otherId in this.tabs) {
				if (otherId != id) {
					toFocus = otherId;
					if (breakNext) {
						break;
					}
				} else {
					if (toFocus) {
						break;
					} else {
						breakNext = true;
					}
				}
			}
			if (toFocus) {
				var tabToFocus = this.tabs[toFocus];
				this._focusTab(toFocus);
			} else {
				this.focused = null;
			}
		} else {
			this.focused = null;
		}
	}
	this.tabs[id].close();
	delete this.tabs[id];
	this.numTabs--;
	chatTabSlider.close(id);
}, uiChanged:function () {
	if (this.useUICookieCache) {
		this.uiChangeTime = presence.getTime();
		presence.doSync();
	}
}, closeTab:function (id) {
	this._closeTab(id);
	this._sendTabStateChange({"close_chat":id});
	this.uiChanged();
}, showVideoChatTab:function () {
	if (presence.inVideoChat) {
		this.tabs[this.focused].showChat();
	}
}, _unfocus:function () {
	if (!this.focused) {
		return false;
	}
	if (presence.inPopoutWindow) {
		this.tabs[this.focused].deselectPopoutChat();
	}
	this.tabs[this.focused].unfocus();
	this.focused = null;
	return true;
}, unfocus:function () {
	var changed = this._unfocus();
	if (changed) {
		this._sendTabStateChange({"unfocus_chat":1});
		this.uiChanged();
	}
	this.lastFocused = null;
}, unfocusNoSync:function () {
	this._unfocus();
}, refocus:function () {
	if (!this.lastFocused || !this.tabs[this.lastFocused]) {
		return null;
	}
	this._focusTab(this.lastFocused);
}, _focusTab:function (id, name, firstName) {
	if (id == this.focused || presence.inVideoChat && id != presence.inVideoChatWith) {
		return;
	}
	if (!this.tabs[id]) {
		if (typeof name == "undefined") {
			if (!this.userInfos[id] || !this.userInfos[id].name) {
				presence.warn("chat: couldn't create tab " + id + " since no name is specified");
				return;
			}
			name = this.userInfos[id].name;
			firstName = this.userInfos[id].firstName;
		}
		this.tabs[id] = new ChatTab(this, id, name, firstName, 0);
		this.numTabs++;
		chatTabSlider.addTab(id);
	}
	chatTabSlider.gotoTab(id);
	if (this.focused) {
		this.tabs[this.focused].unfocus();
		if (presence.inPopoutWindow) {
			this.tabs[this.focused].deselectPopoutChat();
		}
	}
	this.focused = id;
	this.lastFocused = id;
	if (this.focused) {
		var loaded = this.loaded;
		(function () {
			var hidden = !presence.inPopoutWindow && presence.poppedOut;
			if (this.tabs[this.focused]) {
				this.tabs[this.focused].focus(hidden, loaded);
			}
			if (presence.inPopoutWindow) {
				this.tabs[id].selectPopoutChat();
			}
		}).bind(this).defer();
	}
}, focusTab:function (id, name, firstName) {
	presence.pauseSync();
	this._focusTab(id, name, firstName);
	this._sendTabStateChange({"focus_chat":id});
	this.uiChanged();
	this.chatActivityTime = (new Date()).getTime();
	this.doStopBlinking();
	presence.resumeSync();
}, toggleTab:function (id, name, firstName) {
	if (this.focused == id) {
		this.unfocus();
	} else {
		this.focusTab(id, name, firstName);
	}
}, doBlink:function () {
	if (!this.favIcon) {
		var links = document.getElementsByTagName("link");
		for (var i = 0; i < links.length; i++) {
			if (links[i].rel == "shortcut icon") {
				this.favIcon = links[i];
				this.altFavIcon = document.createElement("link");
				this.altFavIcon.rel = "shortcut icon";
				this.realTitle = document.title;
				break;
			}
		}
	}
	if (this.favIcon.parentNode) {
		if (this.newMsgNames && this.newMsgNames.length > 0) {
			if (this.newMsgNamesIndex >= this.newMsgNames.length) {
				this.newMsgNamesIndex = 0;
			}
			var name = this.newMsgNames[this.newMsgNamesIndex++];
			document.title = _tx("New message from {name}!", {"name":name});
		} else {
			document.title = _tx("New message!");
		}
		var p = this.favIcon.parentNode;
		p.removeChild(this.favIcon);
		p.appendChild(this.altFavIcon);
	} else {
		document.title = this.realTitle;
		var p = this.altFavIcon.parentNode;
		p.removeChild(this.altFavIcon);
		p.appendChild(this.favIcon);
	}
}, doStopBlinking:function (force) {
	if (this.blinkingTimer || force) {
		this._stopBlinking();
		presence.doSync();
	}
}, _stopBlinking:function () {
	if (this.blinkingTimer) {
		if (this.favIcon && !this.favIcon.parentNode) {
			this.doBlink();
		}
		clearInterval(this.blinkingTimer);
		this.blinkingTimer = null;
		this.newMsgNames = [];
		this.newMsgNamesIndex = 0;
	}
}, _onBuddyAvailability:function (type, data) {
	this.handleBuddyAvailability(data.justCameOnline);
}, handleBuddyAvailability:function (justCameOnline) {
	for (var id in this.tabs) {
		this.tabs[id].handleBuddyAvailability(justCameOnline);
	}
}, _handlePresenceMessage:function (type, data) {
	var obj = data.obj;
	if (obj.window_id == presence.windowID) {
		return;
	}
	if (obj.from) {
		if (obj.from == this.user) {
			var id = obj.to;
		} else {
			var id = obj.from;
		}
		var tab = this.tabs[id];
	}
	switch (obj.type) {
	  case "unfocus_chat":
		if (!presence.inVideoChat) {
			this._unfocus();
		}
		break;
	  case "focus_chat":
		if (!presence.inVideoChat) {
			this._focusTab(obj.id);
		}
		break;
	  case "close_chat":
		if (!presence.inVideoChat) {
			this._closeTab(obj.id);
		}
		break;
	  case "msg":
	  case "video":
		if (this.inVideoChat && (obj.from == presence.inVideoChatWith || obj.to == presence.inVideoChatWith)) {
			break;
		}
		if (obj.from == this.user) {
			var name = obj.to_name;
			var firstName = obj.to_first_name ? obj.to_first_name : getFirstName(name);
		} else {
			var name = obj.from_name;
			var firstName = obj.from_first_name ? obj.from_first_name : getFirstName(name);
		}
		this.loadInitialUserInfo(id, name, firstName);
		var fromMe = (obj.from == this.user);
		buddyList.setAvailable(id, fromMe);
		if (!tab) {
			tab = this.tabs[id] = new ChatTab(this, id, name, firstName, 0);
			this.numTabs++;
			chatTabSlider.addTab(id);
			if (!this.focused) {
				this.focusTab(id);
			} else {
				tab.getHistory();
			}
		}
		if (presence.inPopoutWindow || !presence.poppedOut) {
			if (fromMe) {
				this.doStopBlinking(true);
			} else {
				if (this.isWindowFocused) {
					setTimeout(this.doStopBlinking.bind(this, true), 500);
				} else {
					if (!presence.isOpera) {
						this.newMsgNames.push(tab.firstName);
						if (!this.blinkingTimer) {
							this.blinkingTimer = setTimeout(function () {
								this.blinkingTimer = setInterval(this.doBlink.bind(this), this.blinkTime);
							}.bind(this), this.initialBlinkDelay);
						}
					}
				}
			}
		}
		obj.time = obj.msg.time;
		tab.newMsg(obj);
		break;
	  case "mf":
		if (!tab) {
			break;
		}
		tab.newMinifeed(obj);
		break;
	  case "status":
		if (!tab) {
			break;
		}
		tab.updateStatus(obj.status, obj.time, obj.statusTimeRel);
		break;
	  case "typ":
		if (tab) {
			tab.newTyping(obj);
		} else {
			presence.debug("typing message ignored");
		}
		break;
	  default:
		break;
	}
}, handleVisibility:function () {
	for (var id in this.tabs) {
		this.tabs[id].handleVisibility();
	}
}, getHistory:function (id, create) {
	create = create || false;
	if (!this.histories[id] && create) {
		this.histories[id] = [];
	}
	return this.histories[id];
}, _renderServerTime12hr:function (serverTimeMS) {
	var time = new Date();
	time.setTime(serverTimeMS + presence.timeSkew);
	var hours = time.getHours();
	var ampm = "am";
	if (hours >= 12) {
		ampm = "pm";
		hours -= 12;
	}
	if (hours == 0) {
		hours = 12;
	}
	var minutes = time.getMinutes();
	if (minutes < 10) {
		minutes = "0" + minutes;
	}
	var timeStr = hours + ":" + minutes + ampm;
	return timeStr;
}, _renderServerTime24hr:function (serverTimeMS) {
	var time = new Date();
	time.setTime(serverTimeMS + presence.timeSkew);
	var hours = time.getHours();
	if (hours < 10) {
		hours = "0" + hours;
	}
	var minutes = time.getMinutes();
	if (minutes < 10) {
		minutes = "0" + minutes;
	}
	var timeStr = hours + ":" + minutes;
	return timeStr;
}};
function chat_simple_popout() {
	window.open(presence.popoutURL, "fbChatWindow", "status=0,toolbar=0,location=0,menubar=0," + "directories=0,resizable=1,scrollbars=0," + "width=" + Presence.prototype.defWidth + ",height=" + Presence.prototype.defHeight + ",left=" + Presence.prototype.defX + ",top=" + Presence.prototype.defY);
}
function search_typeaheadpro(obj, source, properties) {
	this.parent.construct(this, obj, source, properties);
	this._allowDuplicateFoundCalls = (this.enableSearchResults ? this.enableSearchResults : false);
	this._onunload_registered = false;
}
search_typeaheadpro.extend("typeaheadpro");
search_typeaheadpro.prototype.auto_select = false;
search_typeaheadpro.prototype.less_than_n_chars = false;
search_typeaheadpro.prototype.dirty_results = function () {
	this.parent.dirty_results();
	if (!this._onunload_registered) {
		this._onunload_registered = true;
		onunloadRegister(bind(this, function () {
			this.clear();
			this.blur();
			search_friend_source.hasSubmitted = false;
			search_friend_source.already_logged = false;
			this._onunload_registered = false;
		}));
	}
};
search_typeaheadpro.prototype.show = function () {
	this.dropdown.style.border = "0px none";
	if (this.suggestions.length) {
		CSS.addClass(this.list, "typeahead_list_with_shadow");
	} else {
		CSS.removeClass(this.list, "typeahead_list_with_shadow");
	}
	var typeahead_width = 191;
	if (!this.less_than_n_chars) {
		CSS.addClass(this.dropdown, "typeahead_search");
		this.parent.show();
		var anchor_dimensions = Vector2.getElementDimensions(this.anchor);
		this.dropdown.style.left = (elementX(this.anchor) - (typeahead_width - anchor_dimensions.x) + 4) + "px";
		this.dropdown.style.width = typeahead_width + "px";
	} else {
		this.hide();
	}
};
search_typeaheadpro.prototype.select_suggestion = function (index) {
	this.log_data.sm = this.log_data.sm ? this.log_data.sm : "mouse";
	if (this.suggestions && this.suggestions.length > index) {
		this.log_data.ty = index >= 0 ? this.suggestions[index].ty : "fs";
		this.log_data.i = index >= 0 ? this.suggestions[index].i : "0";
		this.log_data.f = this.suggestions.length > 0 ? 0 : 1;
	}
	this.log_data.si = index;
	search_typeahead_log_data(this);
	if (this.suggestions && this.source.history != undefined && this.suggestions.length > index && index >= 0) {
		this.suggestions[index].o = -1;
		this.source.history[this.suggestions[index].i] = -1;
	}
	return this.parent.select_suggestion.call(this, index);
};
search_typeaheadpro.prototype.hide = function () {
	this.parent.hide();
};
search_typeaheadpro.prototype.search_cache = function (text) {
	return undefined;
};
search_typeaheadpro.prototype.found_suggestions = function (suggestions, text, fake_data) {
	var auto_select = ADVANCED_SEARCH_TYPEAHEAD && suggestions && suggestions.length == 1;
	if (auto_select) {
		suggestions.push({t:text, i:text.trim().replace(/ /g, "+"), ty:"search"});
	}
	this.parent.found_suggestions(suggestions, text, fake_data);
	if (suggestions.length > 0 && suggestions[0].ty == "search") {
		this.parent.set_suggestion(0);
	}
	if (this.suggestion_count > 0) {
		CSS.addClass(this.list.firstChild.firstChild, "suggestions_top_border");
		CSS.removeClass(this.list, "no_border_list");
		CSS.addClass(this.list.lastChild.lastChild, "suggestions_bottom_border");
	} else {
		CSS.addClass(this.list, "no_border_list");
	}
	if (this.enableSearchResults) {
		if (this.loadingResults && this.suggestion_count < this.max_results) {
			if (this.list.innerHTML.indexOf("typeahead_search_loading") == -1) {
				this.list.innerHTML += "<div class=\"typeahead_search_loading\">" + _tx("Searching Facebook...") + "</div>";
			}
			CSS.addClass(this.dropdown, "typeahead_search_loading");
			this.showingLoadingIndicator = true;
		} else {
			CSS.removeClass(this.dropdown, "typeahead_search_loading");
			this.showingLoadingIndicator = false;
		}
	}
	if (auto_select) {
		this.set_suggestion(0);
	}
};
search_typeaheadpro.prototype._onkeydown = function (e) {
	this.last_key = e ? event_get_keypress_keycode(e) : -1;
	this.interactive = true;
	if (this.last_key == KEYS.TAB && this.suggestions.length > 0) {
		var next_tab_index = this.selectedindex + (e.shiftKey ? -1 : 1);
		if (next_tab_index < -1 || next_tab_index >= this.suggestions.length) {
			return true;
		}
		this.log_data.kt += 1;
		return false;
	}
	this.parent._onkeydown(e);
};
search_typeaheadpro.prototype._onkeypress = function (e) {
	this.last_key = e ? event_get_keypress_keycode(e) : -1;
	this.interactive = true;
	if (this.last_key == KEYS.TAB && this.suggestions.length > 0) {
		var next_tab_index = this.selectedindex + (e.shiftKey ? -1 : 1);
		if (next_tab_index >= -1 && next_tab_index < this.suggestions.length) {
			this.set_suggestion(next_tab_index);
			this.last_key_suggestion = (new Date()).getTime();
			return false;
		}
	}
	return this.parent._onkeypress(e);
};
function search_typeahead_onselect(friend) {
	if (friend && !search_friend_source.hasSubmitted) {
		search_friend_source.hasSubmitted = true;
	} else {
		return false;
	}
	var url = friend.u;
	if (!url) {
		var type_info = search_friend_source.url_templates[friend.ty];
		if (type_info) {
			url = sprintf(type_info.default_url, escapeURI(friend.i));
		}
	}
	if (!url) {
		return undefined;
	} else {
		if (url.indexOf("?") != -1) {
			goURI(url + "&ref=ts");
		} else {
			goURI(url + "?ref=ts");
		}
	}
	bind(this, this.blur).defer();
	return false;
}
function search_typeahead_wstest(query) {
	new AsyncSignal("/ajax/search/web.php", {q:query}).send();
}
function search_typeahead_log_data(source) {
	var log_data = source.udata;
	for (var key in source.log_data) {
		log_data[key] = source.log_data[key];
	}
	search_typeahead_log(log_data, "onsubmit");
}
function search_typeahead_onsubmit(friend) {
	return !search_friend_source.hasSubmitted;
}
window.ADVANCED_SEARCH_TYPEAHEAD = false;
function search_friend_source(get_param, manual_init) {
	this.get_param = get_param;
	this.parent.construct(this);
	if (!manual_init) {
		this.initialize();
	}
}
search_friend_source.extend("static_source");
search_friend_source.prototype.text_noinput = search_friend_source.prototype.text_placeholder = search_friend_source.prototype.text_nomatch = "";
search_friend_source.prototype.cache_results = true;
search_friend_source.prototype.updated_pics = {};
search_friend_source.prototype._allowed_types = null;
search_friend_source.prototype.initialize = function (callback) {
	var current_time = (new Date()).getTime();
	new AsyncRequest().setMethod("GET").setReadOnly(true).setURI("/ajax/typeahead_search.php?" + this.get_param).setErrorHandler(function () {
	}).setTransportErrorHandler(function () {
	}).setHandler(function (response) {
		var payload = response.getPayload();
		this.values = payload.entries;
		this.build_index.bind(this).defer();
		search_typeahead_log(this.udata, "getdata", current_time);
		var now_minus_5_min = ((new Date()).getTime() / 1000) - 60 * 5;
		if (payload.gen_time < now_minus_5_min) {
			new AsyncRequest().setMethod("GET").setReadOnly(true).setURI("/ajax/recent_pics.php").setData({ref_time:payload.gen_time}).setErrorHandler(function () {
			}).setTransportErrorHandler(function () {
			}).setHandler(function (response) {
				this.updated_pics = response.getPayload().updated_pics;
			}.bind(this)).send();
		}
		if (callback) {
			callback();
		}
	}.bind(this)).disableNectar().send();
};
search_friend_source.url_templates = {};
search_friend_source.loaded_images = {};
search_friend_source.TYPES = {USER:"u", CONNECTION:"c", FOLLOWER:"fl", PAGE:"p", APP:"a", GROUP:"g", EVENT:"e", SEARCH:"search", WEB:"web"};
search_friend_source.prototype.build_index = function () {
	var history = this.history ? this.history.entries : undefined;
	if (history != undefined) {
		this.values = this.values.each(function (value) {
			value.o = history[value.i] != undefined ? history[value.i] : value.o;
			return value;
		});
	}
	this.parent.build_index();
};
search_friend_source.image_load = function (image_elm, insertion_time, user_id) {
	var current_time = (new Date()).getTime();
	search_friend_source.loaded_images[user_id] = true;
	if ((current_time - insertion_time) < 100) {
		CSS.setOpacity(image_elm, 1);
	} else {
		animation(image_elm).to("opacity", 1).duration(100).go();
	}
};
search_friend_source.prototype.gen_html = function (friend, highlight) {
	var icon_url = friend.it;
	if (!icon_url && friend.ty && search_friend_source.url_templates[friend.ty]) {
		icon_url = search_friend_source.url_templates[friend.ty].icon;
	}
	if (this.updated_pics[friend.i]) {
		icon_url = this.updated_pics[friend.i];
	}
	switch (friend.ty) {
	  case search_friend_source.TYPES.USER:
	  case search_friend_source.TYPES.CONNECTION:
	  case search_friend_source.TYPES.PAGE:
		if (!icon_url) {
			return ["<div>", typeahead_source.highlight_found(friend.t, highlight), "</div><div><small>", friend.n || "&nbsp;", "</small></div>"].join("");
		} else {
			if (!search_friend_source.loaded_images[friend.i]) {
				var insertion_time = (new Date()).getTime();
				var attrs = sprintf("onload=\"search_friend_source.image_load(this, %d, %d);\" style=\"opacity:0;filter:alpha(opacity=0);\"", insertion_time, friend.i);
			}
			return ["<img ", attrs, " alt=\"\" src=\"", icon_url, "\"/>", "<div class=\"with_pic\"><span>", typeahead_source.highlight_found(friend.t, highlight), "</span><small>", friend.n || "&nbsp;", "</small></div>"].join("");
		}
		break;
	  case search_friend_source.TYPES.SEARCH:
		return ["<div class=\"app\"><div class=\"icon\" style=\"background-image: url(", icon_url, ")\">&nbsp;</div>", "<span>", friend.t, "</span></div>"].join("");
		break;
	  default:
		return ["<div class=\"app clearfix\"><div class=\"icon\" style=\"background-image: url(", icon_url, ")\">&nbsp;</div>", "<span>", typeahead_source.highlight_found(friend.t, highlight), "</span></div>"].join("");
	}
};
search_friend_source.prototype.allowTypes = function (type_arr) {
	this._allowed_types = type_arr;
	return this;
};
search_friend_source.prototype.search_value = function (text, searching) {
	var results;
	if (this.owner.enableSearchResults) {
		if (this._searchQueryTimer) {
			clearTimeout(this._searchQueryTimer);
			this._searchQueryTimer = null;
		}
	}
	var enough_tokens = false;
	var tokens = typeahead_source.tokenize(text);
	for (var i = 0; i < tokens.length; i++) {
		if (tokens[i] != "") {
			enough_tokens = true;
			break;
		}
	}
	if (enough_tokens) {
		this.owner.less_than_n_chars = false;
		results = this.parent.search_value(text);
	} else {
		if (this.is_ready) {
			this.owner.less_than_n_chars = true;
			results = [];
		}
	}
	var non_people_search = (results && results.length) ? false : true;
	if (results) {
		for (var i = 0; i < results.length; i++) {
			if ((results[i].ty != search_friend_source.TYPES.USER) && (results[i].ty != search_friend_source.TYPES.CONNECTION)) {
				non_people_search = true;
				break;
			}
		}
	}
	if (this.owner.enableSearchResults) {
		if (!searching && !(results && results.length > (this.search_limit - 1))) {
			this._searchQueryTimer = setTimeout(this.search_value.bind(this, text, true), 300);
			if (!results || (results && results.length == 0)) {
				this.owner.set_message(_tx("Searching Facebook..."));
				this.owner.show();
				return;
			}
			this.owner.loadingResults = true;
		}
		if (searching && !(results && results.length > (this.search_limit - 1))) {
			new AsyncRequest().setMethod("GET").setReadOnly(true).setData({q:text}).setURI("/ajax/universal_search.php").setHandler(function (text, results, response) {
				if (this._searchQueryTimer) {
					return;
				}
				this.owner.loadingResults = false;
				var new_results = response.payload.results;
				if (!results) {
					var results = [];
				}
				for (var i = 0; i < new_results.length; i++) {
					if (results.length > (this.search_limit - 1)) {
						break;
					}
					results.push(new_results[i]);
				}
				this.owner.found_suggestions(results, text, false);
			}.bind(this, text, results)).send();
			return;
		}
	}
	if (results && non_people_search && enough_tokens && search_friend_source.WEBSEARCH_USER) {
		results.push({t:_tx("Search Facebook"), ty:search_friend_source.TYPES.SEARCH, i:text});
		results.push({t:_tx("Search the Web"), ty:search_friend_source.TYPES.WEB, i:text});
	}
	if (results && this._allowed_types) {
		results = results.filter(function (result) {
			return this._allowed_types.contains(result.ty);
		}.bind(this));
	}
	return results;
};
search_friend_source.prototype._sort_text_obj = function (a, b) {
	var history = this.history ? this.history.entries : undefined;
	if (history != undefined) {
		a.o = history[a.i] || a.o;
		b.o = history[b.i] || b.o;
	}
	if (a.o != b.o) {
		return a.o - b.o;
	}
	return a.t.localeCompare(b.t);
};
search_friend_source.WEBSEARCH_USER = false;
window.ADVANCED_SEARCH_TYPEAHEAD = false;
function ApplicationDock(bookmarks, recentApps, bookmarkableApp) {
	this.sortedBookmarks = [];
	this.undoTimeouts = {};
	this.menuLoaded = false;
	this.menuOpen = false;
	this.menuWrapperID = "presence_applications";
	this.applicationTabID = "presence_applications_tab";
	this.applicationMenuContentID = "presence_applications_content";
	this.menuWrapper = $(this.menuWrapperID, true);
	this.applicationTab = $(this.applicationTabID, true);
	this.applicationMenuContainer = $(this.applicationMenuContentID, true);
	this.iconGardenRoot = $("application_icon_garden_root", true);
	this.bookmarkableAppWrapper = $("bookmarkable_app", true);
	this.bookmarkableAppContainer = $("presence_applications_bookmark_app", true);
	this.sortableRoot = null;
	this.sortableGroup = null;
	this.extendedSortableGroup = null;
	this.extendedSortableRoot = null;
	this.iconGardenSortableGroup = null;
	this.currentIconGardenDraggableKey = null;
	this.recentSortableGroup = null;
	this.recentSortableRoot = null;
	this.lastBookmarkedApp = null;
	this.logTabOpen = false;
	this.logBMClick = false;
	this.topBookmark = null;
	this.recentApps = null;
	this.sortedRecentApps = null;
	this.bookmarks = bookmarks;
	this.recentApps = recentApps;
	this.bookmarkableApp = bookmarkableApp;
	this.suppressReset = false;
	this.removedBookmarks = {};
}
copy_properties(ApplicationDock, {NUM_SHOWN:6, BOOKMARKS_CHANGED:"bookmarks_changed", SaveBookmarksSource:{ICON_GARDEN:1, APPLICATION_MENU:2, BOOKMARK_CURRENT_APP:3}, ANIM_DURATION:200, UNDO_ANIM_HIDE_DELAY:5000, RECENT_APPS_ANIM_HIDE_DELAY:1000});
ApplicationDock.prototype = {_init:function () {
	if (!is_empty(this.bookmarkableApp)) {
		this._renderBookmarkableApp();
	}
	(function () {
		this.sortedBookmarks = this._getSortedList();
		this._initIconGardenSortables();
		Arbiter.subscribe(Presence.getArbiterMessageType("bookmarks"), this._handleBookmarkMessage.bind(this));
	}).bind(this).defer();
}, _getSortedList:function (applications) {
	applications = applications || this.bookmarks;
	var array = keys(applications);
	this._sort(array, applications);
	return array;
}, _ensureSortedRecentApps:function () {
	if (!this.sortedRecentApps) {
		this.sortedRecentApps = this._getSortedList(this.recentApps);
	}
}, _sort:function (array, applications) {
	var compareFunction = this._sortApps.bind(null, applications);
	array.sort(compareFunction);
}, _sortApps:function (applications, id1, id2) {
	return applications[id1].order - applications[id2].order;
}, _renderApplicationMenu:function () {
	DOM.setContent(this.applicationMenuContainer, HTML(this._renderAppMenuContent()));
}, _renderAppMenuContent:function () {
	var markupArr = [];
	var bookmarksDividerRendered = false;
	if (count(this.recentApps) > 0) {
		markupArr.push("<div id=\"application_menu_recent_apps_container\">");
		markupArr.push(this._renderAppMenuDivider(_tx("Recently Used")));
		markupArr.push("<ul id=\"application_menu_recent_apps\">");
		this._ensureSortedRecentApps();
		for (var i = 0; i < this.sortedRecentApps.length; i++) {
			var appID = this.sortedRecentApps[i];
			markupArr.push(this._renderAppMenuItem(appID));
		}
		markupArr.push("</ul>");
		markupArr.push("</div>");
		if (this.sortedBookmarks.length) {
			bookmarksDividerRendered = true;
			markupArr.push(this._renderAppMenuDivider(_tx("Bookmarks")));
		}
	}
	if (this.sortedBookmarks.length) {
		if (!bookmarksDividerRendered) {
			markupArr.push(this._renderAppMenuDivider(_tx("Bookmarks")));
		}
		markupArr.push("<ul id=\"application_menu_root\">");
		for (var i = 0; i < this.sortedBookmarks.length; i++) {
			var appID = this.sortedBookmarks[i];
			if (i == ApplicationDock.NUM_SHOWN) {
				markupArr.push("</ul>", this._renderAppMenuDivider(), "<ul id=\"application_menu_extended_root\">");
			}
			markupArr.push(this._renderAppMenuItem(appID, null, true));
		}
		markupArr.push("</ul>");
	}
	return markupArr.join("");
}, _renderAppMenuItem:function (appID) {
	var removeBookmarkButtonStr = "";
	var undoRemoveStr = "";
	removeBookmarkButtonStr = sprintf("<div class=\"del_button\" id=\"%s\"></div>", this._getAppMenuRemoveItemId(appID));
	undoRemoveStr = ["<div class=\"app_menu_undo_remove_bookmark\" id=\"", this._getAppMenuUndoRemoveItemId(appID), "\"><div class=\"app_menu_undo_remove_bookmark_link\"><a href=\"#\" id=\"", this._getAppMenuUndoRemoveItemLinkId(appID), "\">", _tx("Undo"), "</a></div><div>", _tx("Bookmark removed"), "</div></div>"].join("");
	return ["<li id=\"", this._getApplicationMenuItemId(appID), "\"><div id=\"", this._getApplicationMenuLinkContainerId(appID), "\">", removeBookmarkButtonStr, this._renderApplicationMenuItem(appID), "</div>", undoRemoveStr, "</li>", ].join("");
}, _renderAppMenuDivider:function (dividerText) {
	return ["<div class=\"application_menu_divider\">", dividerText || "", "</div>"].join("");
}, _renderApplicationMenuItem:function (appID) {
	var appInfo = this._getAppInfo(appID);
	var isFacebookURI = URI(appInfo.href || "").isFacebookURI();
	var markupArr = ["<a id=\"", this._getApplicationMenuLinkId(appID), "\"", isFacebookURI ? "" : " target=\"_blank\"", " href=\"", appInfo.href, "\" class=\"item\">", "<div class=\"item\">", appInfo.new_icon_img, "</div>", htmlize(appInfo.name), "</a>"];
	return markupArr.join("");
}, _getApplicationMenuItemId:function (appID) {
	return "application_menu_" + appID;
}, _getApplicationMenuLinkContainerId:function (appID) {
	return "application_menu_link_container_" + appID;
}, _getApplicationMenuLinkId:function (appID) {
	return "application_menu_link_" + appID;
}, render:function () {
	this._init();
	return this;
}, toggleTab:function () {
	if (!this.menuOpen) {
		this._openTab();
	} else {
		this._closeTab();
	}
}, _openTab:function () {
	if (this.menuOpen) {
		return;
	}
	if (!this.menuLoaded) {
		this._renderApplicationMenu();
		this._initApplicationMenu.bind(this).defer();
		this.menuLoaded = true;
	}
	presence.openTab(this.menuWrapperID, this.applicationTabID, this.applicationMenuContentID);
	this.menuOpen = true;
	if (this.logTabOpen) {
		AsyncRequest.pingURI("/ajax/abtest_multi_async.php", {test_names:["PLATFORM_APPDOCK_APPBUTTON", "APPS_MENU_TEST"], actions:["APPLICATIONS_MENU_OPEN"]});
	}
}, _closeTab:function () {
	presence.toggleTab(this.menuWrapperID, this.applicationTabID, this.applicationMenuContentID);
	this.menuOpen = false;
}, _initApplicationMenu:function () {
	this.sortableRoot = $("application_menu_root");
	this.sortableGroup = new SortableGroup();
	var source = ApplicationDock.SaveBookmarksSource.APPLICATION_MENU;
	this.sortableGroup.setOrderChangeHandler(this._saveBookmarksOrder.bind(this, source));
	var sortableGroup = this.sortableGroup;
	for (var i = 0; i < this.sortedBookmarks.length; i++) {
		var appID = this.sortedBookmarks[i];
		if (i == ApplicationDock.NUM_SHOWN) {
			this._initExtendedApplicationMenuSortables();
			sortableGroup = this.extendedSortableGroup;
		}
		this._addApplicationMenuSortable(appID, sortableGroup);
		this._initEventHandlers(appID);
	}
	if (this.recentApps && count(this.recentApps) > 0) {
		this.recentSortableRoot = $("application_menu_recent_apps");
		this.recentSortableGroup = new SortableGroup();
		this.recentSortableGroup.setDroppable(false);
		this.sortableGroup.link(this.recentSortableGroup);
		for (var appID in this.recentApps) {
			this._addApplicationMenuSortable(appID, this.recentSortableGroup);
		}
	}
}, _initEventHandlers:function (appID) {
	var menuLinkID = this._getApplicationMenuLinkId(appID);
	$(menuLinkID).listen("click", this._menuClick.bind(this, appID, menuLinkID));
	var item = $(this._getApplicationMenuItemId(appID));
	item.listen("mouseover", CSS.addClass.bind(CSS, item, "hover"));
	item.listen("mouseout", CSS.removeClass.bind(CSS, item, "hover"));
	this._registerRemoveItemHandler(appID, this._getAppMenuRemoveItemId(appID), false);
	this._registerRemoveItemHandler(appID, this._getAppMenuUndoRemoveItemLinkId(appID), true);
}, _registerRemoveItemHandler:function (appID, elemID, setting) {
	$(elemID).listen("click", (function (e) {
		this._setBookmarked(appID, setting);
		return $E(e).kill();
	}).bind(this));
}, _setBookmarked:function (appID, setting) {
	this.suppressReset = true;
	var sortableGroup;
	var item = ge(this._getApplicationMenuItemId(appID));
	if (item.parentNode == this.sortableRoot) {
		sortableGroup = this.sortableGroup;
	} else {
		sortableGroup = this.extendedSortableGroup;
	}
	if (setting) {
		this._addApplicationMenuSortable(appID, sortableGroup);
		var source = ApplicationDock.SaveBookmarksSource.APPLICATION_MENU;
		this._saveBookmarksOrder.bind(this, source).defer();
		delete this.removedBookmarks[appID];
	} else {
		if (!this.removedBookmarks[appID]) {
			this.removedBookmarks[appID] = this.bookmarks[appID];
		}
		sortableGroup.removeSortable(appID);
		this._removeBookmark(appID);
	}
	this._animateSetBookmarked(appID, setting);
}, _removeBookmark:function (appID) {
	new AsyncRequest().setURI("/ajax/edit_app_settings.php").setData({"bookmark":1, "bookmarked":false, "app_id":appID}).setHandler(this._removeBookmarkHandler.bind(this, appID)).send();
}, _removeBookmarkHandler:function (appID) {
	this.sortedBookmarks.remove(appID);
	this._resetIconGarden();
}, _animateSetBookmarked:function (appID, setting) {
	var itemElem = ge(this._getApplicationMenuLinkContainerId(appID));
	var undoElem = ge(this._getAppMenuUndoRemoveItemId(appID));
	var hiddenElem, shownElem;
	if (!setting) {
		hiddenElem = itemElem;
		shownElem = undoElem;
	} else {
		hiddenElem = undoElem;
		shownElem = itemElem;
	}
	hide(hiddenElem);
	animation(shownElem).show().from("opacity", 0).to("opacity", 1).duration(ApplicationDock.ANIM_DURATION).go();
	if (!setting) {
		this.undoTimeouts[appID] = (function () {
			var item = ge(this._getApplicationMenuItemId(appID));
			if (item) {
				animation(item).to("height", 0).duration(ApplicationDock.ANIM_DURATION).hide().ondone(this._onsortableLinkJump.bind(this, null)).go();
			}
		}).bind(this).defer(ApplicationDock.UNDO_ANIM_HIDE_DELAY);
	} else {
		clearTimeout(this.undoTimeouts[appID]);
		delete this.undoTimeouts[appID];
	}
}, _menuClick:function (appID, menuLinkID) {
	if (ApplicationDock.presenceWidthTest) {
		AsyncRequest.pingURI("/ajax/abtest_async.php", {test_name:"APP_DOCK_TEST", action:"USED_APP_MENU"});
	}
	var appInfo = this._getAppInfo(appID);
	var logAction = appInfo.logAction;
	if (logAction) {
		AsyncRequest.pingURI("/ajax/abtest_async.php", {test_name:"APPS_MENU_TEST", action:logAction});
	}
}, _getAppInfo:function (appID) {
	return this.bookmarks[appID] || this.recentApps[appID] || this.removedBookmarks[appID];
}, _initExtendedApplicationMenuSortables:function () {
	this.extendedSortableGroup = new SortableGroup();
	this.extendedSortableRoot = $("application_menu_extended_root");
	this.sortableGroup.link(this.extendedSortableGroup);
	this.sortableGroup.setLinkJumpHandler(this._onsortableLinkJump.bind(this));
	var source = ApplicationDock.SaveBookmarksSource.APPLICATION_MENU;
	this.extendedSortableGroup.setOrderChangeHandler(this._saveBookmarksOrder.bind(this, source));
}, _addApplicationMenuSortable:function (appID, sortableGroup) {
	var obj = $(this._getApplicationMenuItemId(appID));
	var handle = $(this._getApplicationMenuLinkId(appID));
	sortableGroup.addSortable(appID, obj, handle);
}, _resetApplicationMenu:function () {
	if (!this.menuLoaded) {
		return;
	}
	this.sortableGroup.destroy();
	if (this.extendedSortableGroup) {
		this.extendedSortableGroup.destroy();
	}
	if (this.recentSortableGroup) {
		this.recentSortableGroup.destroy();
	}
	this._renderApplicationMenu();
	this._initApplicationMenu.bind(this).defer();
}, _onsortableLinkJump:function (key) {
	var order = this.sortableGroup.getOrder(), migrateKey = null;
	if (order.length > ApplicationDock.NUM_SHOWN) {
		for (var i = order.length - 1; i >= 0; i--) {
			if (order[i] != key) {
				migrateKey = order[i];
				break;
			}
		}
		DOM.prependContent(this.extendedSortableRoot, $(this._getApplicationMenuItemId(migrateKey)));
		this.extendedSortableGroup.migrateLinkedSortable(migrateKey);
	} else {
		if (order.length < ApplicationDock.NUM_SHOWN) {
			order = this.extendedSortableGroup.getOrder();
			for (var i = 0; i < order.length; i++) {
				if (order[i] != key) {
					migrateKey = order[i];
					break;
				}
			}
			DOM.appendContent(this.sortableRoot, $(this._getApplicationMenuItemId(migrateKey)));
			this.sortableGroup.migrateLinkedSortable(migrateKey);
		}
	}
	var order = this.recentSortableGroup.getOrder();
	if (order.length < this.sortedRecentApps.length) {
		this.recentAppBookmarked = key;
	} else {
		if (this.recentAppBookmarked) {
			delete this.recentAppBookmarked;
		}
	}
}, _saveBookmarksOrder:function (source) {
	this._processNewBookmark();
	var order = this._getOrder(source);
	new AsyncRequest().setURI("/ajax/edit_app_settings.php").setData({reorder_bookmarks:1, app_ids:order}).setHandler(this._saveBookmarksHandler.bind(this, order, source)).send();
}, _processNewBookmark:function () {
	if (this.recentAppBookmarked) {
		var appID = this.recentAppBookmarked;
		this.sortedRecentApps.remove(appID);
		this._initEventHandlers(appID);
		this.bookmarks[appID] = this.recentApps[appID];
		delete this.recentApps[appID];
		delete this.recentAppBookmarked;
		if (!this.sortedRecentApps.length) {
			(function () {
				animation($("application_menu_recent_apps_container")).to("height", 0).to("opacity", 0).duration(ApplicationDock.ANIM_DURATION).hide().go();
			}).defer(ApplicationDock.RECENT_APPS_ANIM_HIDE_DELAY);
		}
	}
}, _getOrder:function (source) {
	if (source == ApplicationDock.SaveBookmarksSource.APPLICATION_MENU) {
		order = this.sortableGroup.getOrder();
	} else {
		order = this.iconGardenSortableGroup.getOrder();
		if (source == ApplicationDock.SaveBookmarksSource.BOOKMARK_CURRENT_APP) {
			order.push(this._getAppShaftedByBookmarkableApp());
		}
	}
	if (this.extendedSortableGroup) {
		order = order.concat(this.extendedSortableGroup.getOrder());
	} else {
		if (!this.menuLoaded && this.sortedBookmarks.length > ApplicationDock.NUM_SHOWN) {
			order = order.concat(this.sortedBookmarks.slice(ApplicationDock.NUM_SHOWN));
		}
	}
	order = unique(order);
	return order;
}, _saveBookmarksHandler:function (appIDs, source) {
	this._updateSortedList(appIDs);
	switch (source) {
	  case ApplicationDock.SaveBookmarksSource.APPLICATION_MENU:
		this._resetIconGarden();
		break;
	  case ApplicationDock.SaveBookmarksSource.ICON_GARDEN:
	  case ApplicationDock.SaveBookmarksSource.BOOKMARK_CURRENT_APP:
		this._resetApplicationMenu();
		if (this.lastBookmarkedApp) {
			this._openTab();
			var menuItem = $(this._getApplicationMenuItemId(this.lastBookmarkedApp));
			var gardenItem = $(this._getApplicationIconGardenItemId(this.lastBookmarkedApp));
			animation(menuItem).from("backgroundColor", "#FFF9B7").to("backgroundColor", "#FFFFFF").ease(animation.ease.begin).duration(4000).ondone(CSS.setStyle.bind(this, menuItem, "backgroundColor", "")).go();
			animation(gardenItem).from("backgroundColor", "#FFF9B7").to("backgroundColor", "#EAEAEA").ease(animation.ease.begin).duration(4000).ondone(CSS.setStyle.bind(this, gardenItem, "backgroundColor", "")).go();
		}
		break;
	}
}, _updateSortedList:function (appIDs) {
	this.sortedBookmarks = [];
	for (var i = 0; i < appIDs.length; i++) {
		this.sortedBookmarks.push(appIDs[i]);
	}
}, _getIconGardenElem:function (appID) {
	var appInfo = this._getAppInfo(appID);
	var link = $N("a", {id:this._getApplicationIconGardenLinkId(appID), href:appInfo.href}, HTML(appInfo.new_icon_img + "<div class=\"titletip\">" + "<strong>" + htmlize(appInfo.name) + "</strong>" + "</div>"));
	link.listen("mouseover", this.mouseOverIconGarden.bind(this, appID));
	link.listen("mouseout", this.mouseOutIconGarden.bind(this, appID));
	if (!URI(appInfo.href || "").isFacebookURI()) {
		link.setAttribute("target", "_blank");
	}
	return $N("div", {id:this._getApplicationIconGardenItemId(appID), className:"icon_garden_elem"}, link);
}, mouseOverIconGarden:function (appID) {
	if (shown(this.menuWrapper)) {
		return;
	}
	if (!this.currentIconGardenDraggableKey || this.currentIconGardenDraggableKey != appID) {
		CSS.addClass(this._getApplicationIconGardenItemId(appID), "hover");
	}
}, mouseOutIconGarden:function (appID) {
	CSS.removeClass(this._getApplicationIconGardenItemId(appID), "hover");
}, _grabCallback:function (appID) {
	this.currentIconGardenDraggableKey = appID;
}, _dropCallback:function (appID) {
	this.currentIconGardenDraggableKey = null;
}, _getApplicationIconGardenItemId:function (appID) {
	return "application_icon_garden_" + appID;
}, _getApplicationIconGardenLinkId:function (appID) {
	return "application_icon_garden_link_" + appID;
}, _getAppMenuRemoveItemId:function (appID) {
	return "application_remove_item_" + appID;
}, _getAppMenuUndoRemoveItemId:function (appID) {
	return "application_undo_remove_item_" + appID;
}, _getAppMenuUndoRemoveItemLinkId:function (appID) {
	return "application_undo_remove_item_link_" + appID;
}, _initIconGardenSortables:function () {
	this.iconGardenSortableGroup = new SortableGroup();
	var source = ApplicationDock.SaveBookmarksSource.ICON_GARDEN;
	this.iconGardenSortableGroup.setOrderChangeHandler(this._saveBookmarksOrder.bind(this, source));
	for (var i = 0, length = Math.min(this.sortedBookmarks.length, ApplicationDock.NUM_SHOWN); i < length; i++) {
		var appID = this.sortedBookmarks[i];
		this._addIconGardenSortable(appID);
	}
}, _addIconGardenSortable:function (appID) {
	var obj = $(this._getApplicationIconGardenItemId(appID));
	var handle = $(this._getApplicationIconGardenLinkId(appID));
	this.iconGardenSortableGroup.addSortable(appID, obj, handle).setGrabCallback(this._grabCallback.bind(this)).setDropCallback(this._dropCallback.bind(this));
	if (ApplicationDock.presenceWidthTest) {
		handle.listen("click", function () {
			AsyncRequest.pingURI("/ajax/abtest_async.php", {action:"USED_APP_DOCK", test_name:"APP_DOCK_TEST"});
		});
	}
	if (this.logBMClick || this.logTabOpen) {
		var actions = ["APPLICATIONS_GARDEN_CLICK"];
		var test_names = ["PLATFORM_APPDOCK_APPBUTTON", "PLATFORM_BOOKMARK_BUTTON_LOCATION", "APPS_MENU_TEST"];
		handle.listen("click", AsyncRequest.pingURI.bind(AsyncRequest, "/ajax/abtest_multi_async.php", {"actions":actions, "test_names":test_names}));
	}
}, _resetIconGarden:function () {
	this.iconGardenSortableGroup.destroy();
	DOM.empty(this.iconGardenRoot);
	for (var i = 0, length = Math.min(this.sortedBookmarks.length, ApplicationDock.NUM_SHOWN); i < length; i++) {
		var appID = this.sortedBookmarks[i];
		DOM.appendContent(this.iconGardenRoot, this._getIconGardenElem(appID));
	}
	this._initIconGardenSortables.bind(this).defer();
}, _renderBookmarkableApp:function () {
	DOM.setContent(this.bookmarkableAppContainer, HTML(this._renderBookmarkableAppContent()));
	var link = DOM.find(this.bookmarkableAppContainer, "a");
	link.listen("click", this.bookmarkCurrentApp.bind(this, false));
	if (this.topBookmark && ge("sidebar_ads")) {
		this.bookmarkTopContainer = $N("div", {"className":"bookmark_top_container"});
		DOM.setContent(this.bookmarkTopContainer, HTML(this.topBookmark));
		var bookmarkTop = DOM.find(this.bookmarkTopContainer, "div.bookmark_dummy");
		DOM.insertBefore(this.bookmarkTopContainer, $("sidebar_ads"));
		DOM.replace(bookmarkTop, HTML(this._renderBookmarkableAppContent("top")));
		var link = DOM.find(this.bookmarkTopContainer, "a");
		link.listen("click", this.bookmarkCurrentApp.bind(this, true));
	}
	CSS.removeClass(this.bookmarkableAppWrapper, "hidden_elem");
}, _hideBookmarkableApp:function () {
	CSS.addClass(this.bookmarkableAppWrapper, "hidden_elem");
	if (this.bookmarkTopContainer) {
		hide(this.bookmarkTopContainer);
	}
}, _renderBookmarkableAppContent:function () {
	var appInfo = values(this.bookmarkableApp)[0];
	var markupArr = ["<a", " onmouseover=\"applicationDock.mouseOverBookmarkableApp();return false;\"", " onmouseout=\"applicationDock.mouseOutBookmarkableApp();return false;\"", " href=\"#\">", "<span class=\"bookmark_app_plus\"></span>", appInfo.new_icon_img, "<span class=\"bookmark_text\">", _tx("Bookmark"), " ", htmlize(appInfo.name), "</span>", "</a>"];
	return markupArr.join("");
}, mouseOverBookmarkableApp:function () {
	CSS.addClass(this.bookmarkableAppWrapper, "hover");
}, mouseOutBookmarkableApp:function () {
	CSS.removeClass(this.bookmarkableAppWrapper, "hover");
}, setBookmarkableApp:function (bookmarkableApp) {
	this.bookmarkableApp = bookmarkableApp;
	if (is_empty(bookmarkableApp)) {
		this._hideBookmarkableApp();
	} else {
		this._renderBookmarkableApp();
	}
	return this;
}, setTopBookmarkWrap:function (topBookmarkWrap) {
	this.topBookmark = topBookmarkWrap;
	return this;
}, setADLogging:function (logState) {
	this.logTabOpen = logState;
	return this;
}, setBMLogging:function (logState) {
	this.logBMClick = logState;
	return this;
}, _getAppShaftedByBookmarkableApp:function () {
	return this.sortedBookmarks[ApplicationDock.NUM_SHOWN - 1];
}, bookmarkCurrentApp:function (fromTop) {
	var appID = keys(this.bookmarkableApp)[0];
	var existingApp = this.bookmarks[appID];
	this.bookmarks[appID] = this.bookmarkableApp[appID];
	var div = this._getIconGardenElem(appID);
	if (this.sortedBookmarks.length >= ApplicationDock.NUM_SHOWN) {
		this.iconGardenRoot.replaceChild(div, this.iconGardenRoot.lastChild);
		this.iconGardenSortableGroup.removeSortable(this._getAppShaftedByBookmarkableApp());
	} else {
		this.iconGardenRoot.appendChild(div);
	}
	this._addIconGardenSortable.bind(this, appID).defer();
	this._hideBookmarkableApp();
	this.lastBookmarkedApp = appID;
	if (this.logBMClick) {
		var result = {test_name:"PLATFORM_BOOKMARK_BUTTON_LOCATION"};
		result.action = fromTop ? "PLATFORM_BOOKMARK_TOP" : "PLATFORM_BOOKMARK_BOTTOM";
		AsyncRequest.pingURI("/ajax/abtest_async.php", result);
	}
	this._saveBookmarksOrder.bind(this, ApplicationDock.SaveBookmarksSource.BOOKMARK_CURRENT_APP).defer();
}, _handleBookmarkMessage:function (type, data) {
	var obj = data.obj;
	var newSortedList = this._getSortedList(obj.bookmarks);
	if (!are_equal(this.sortedBookmarks, newSortedList)) {
		this.bookmarks = obj.bookmarks;
		this.sortedBookmarks = newSortedList;
		if (!this.suppressReset) {
			this._resetApplicationMenu();
			this._resetIconGarden();
		}
		this.suppressReset = false;
	}
	var message = {sender:this, applications:this.sortedBookmarks};
	Arbiter.inform(ApplicationDock.BOOKMARKS_CHANGED, message);
}};
function Beep(owner, properties) {
	copy_properties(this, properties);
	this.unread = true;
	this._owner = owner;
	this._node = $(HTML(this.html).getNodes()[0]);
	CSS.setOpacity(this._node, "0");
	this._node.find("a.beeper_x").listen("click", this.remove.bind(this));
	var elements = this._node.scry("a.undo_link");
	if (elements.length == 1) {
		elements[0].listen("click", this.onUndoClick.bind(this, elements[0]));
	}
	this._node.listen("mouseover", this.onMouseOver.bind(this));
	this._node.listen("mouseout", this.onMouseOut.bind(this));
}
copy_properties(Beep, {REMOVE_BEEP_ANIM_MS:250, UNDONE_BEEP_DELAY_MS:1000});
copy_properties(Beep.prototype, {onMouseOver:function (event) {
	this._node.addClass("UIBeep_Selected");
	this._selected = true;
	this.setEdges();
}, onMouseOut:function (event) {
	this._node.removeClass("UIBeep_Selected");
	this._selected = false;
	this.setEdges();
}, onUndoClick:function (link) {
	new AsyncRequest().setURI("/ajax/presence/undo_send.php").setData({alert_id:this.alertId}).setHandler(function () {
		DOM.replace(link, $N("span", {className:"undo_link"}, _tx("Undone")));
		this.remove.bind(this).defer(Beep.UNDONE_BEEP_DELAY_MS);
	}.bind(this)).send();
}, xout:function () {
	this._owner.pause();
	var cancel_handler = function () {
		this._owner.unpause();
	}.bind(this);
	ChatNotifications.showReportDialog(this.applicationName, this.onHideBeepSpam.bind(this, false), this.onHideBeepSpam.bind(this, true), cancel_handler);
}, onHideBeepSpam:function (isSpamReport) {
	ChatNotifications.markAppNotificationSpam(this.applicationId, this.platformType, isSpamReport);
	this._owner.hideBeepsByApplication(this.applicationId);
	presenceNotifications.hideNotifications(this.applicationId);
	this._owner.unpause(Beeper.DEFAULT_DELAY_MS);
}, remove:function () {
	animation(this._node).to("width", 30).to("height", 27).to("margin-left", 169).to("opacity", 0).duration(Beep.REMOVE_BEEP_ANIM_MS).ease(animation.ease.both).ondone(function () {
		this._node.remove();
		this._owner.beeps.remove(this);
		if (!this._owner.beeps.length) {
			this._owner.reset();
		} else {
			this._owner.updateEdges();
		}
	}.bind(this)).go();
	this.fadeOut(Beep.REMOVE_BEEP_ANIM_MS);
}, markRead:function () {
	this.unread = false;
}, endFade:function () {
	if (this._node) {
		this._node.removeClass("UIBeep_Fading");
	}
	this._owner.setFading(false, this._isTop, this._isBottom);
}, fadeOut:function (duration) {
	this._node.addClass("UIBeep_Fading");
	this._owner.setFading(true, this._isTop, this._isBottom);
	var thumb = this._node.scry("img.beeper_pic");
	if (thumb && thumb.length) {
		thumb = thumb[0];
		animation(thumb).to("width", 20).to("height", 20).duration(duration).blind().go();
	}
	var icon = this._node.find("div.UIBeep_Icon");
	animation(icon).from("width", 40).to(20).duration(duration).ondone(this.endFade.bind(this)).go();
}, fadeIn:function (duration) {
	this._owner.fadeIn(this._isTop, this._isBottom, duration);
	animation(this._node).to("opacity", 1).ease(animation.ease.both).duration(duration).show().go();
}, setEdges:function () {
	if (this._isTop) {
		this.setTop(true);
	}
	if (this._isBottom) {
		this.setBottom(true);
	}
}, setBottom:function (value) {
	this._isBottom = value;
	CSS.conditionClass(this._node, "UIBeep_Bottom", value);
	if (value) {
		this._owner.setBottom(this._selected);
	}
}, setTop:function (value) {
	this._isTop = value;
	CSS.conditionClass(this._node, "UIBeep_Top", value);
	if (value) {
		this._owner.setTop(this._selected);
	}
}, getNode:function () {
	return this._node;
}});
function Beeper(root) {
	this._baseRoot = root;
	this._receiver = new LiveMessageReceiver(Beeper.BEEP_EVENT).setAppId(Beeper.APP_ID).setHandler(this.fromMessage.bind(this)).register();
	Arbiter.subscribe(Presence.TAB_OPENED, this.onPresenceTabOpen.bind(this));
}
copy_properties(Beeper, {BEEP_EVENT:"beep_event", APP_ID:30729425562, DEFAULT_DELAY_MS:10000, DEFAULT_UNPAUSE_MS:500, SLIDE_DOWN_ANIMATION_MS:700, FADE_IN_ANIMATION_MS:500, FADE_OUT_ANIMATION_MS:1250, _instance:null, setInstance:function (instance) {
	Beeper._instance = instance;
}, getInstance:function () {
	if (!Beeper._instance) {
		throw "Beeper must be initialiazed with .setInstance() first.";
	}
	return Beeper._instance;
}});
copy_properties(Beeper.prototype, {beeps:[], reset:function () {
	this.beeps = [];
	if (this._root) {
		this._root.remove();
	}
	this._root = $(this._baseRoot.cloneNode(true));
	this._root.listen("mouseover", this.mouseover.bind(this));
	this._root.listen("mouseout", this.mouseout.bind(this));
	DOM.insertAfter(this._baseRoot, this._root);
	this._full = this._root.find("div.UIBeeper_Full");
	this._inside = this._root.find("div.Beeps");
	this._top = this._root.find("img.UIBeeper_Top");
	this._top_selected = this._root.find("img.UIBeeper_Top_Selected");
	this._bottom = this._root.find("img.UIBeeper_Bottom");
	this._bottom_selected = this._root.find("img.UIBeeper_Bottom_Selected");
	if (this._timer) {
		clearInterval(this._timer);
	}
	this._timer = null;
	this._animation = null;
	this._animatingOut = false;
	this._paused = 0;
	Arbiter.inform(Arbiter.BEEPS_EXPIRED);
}, onPresenceTabOpen:function (type, data) {
	tab = data.tabID;
	if (tab && tab == "presence_notifications_tab") {
		this.reset();
	}
}, mouseover:function (event) {
	this.pause();
	this.markRead();
}, mouseout:function (event) {
	var target = $E(event).getTarget();
	this.unpause();
}, fromMessage:function (properties) {
	if (!this._root) {
		this.reset();
	}
	var beep = new Beep(this, properties);
	this.addBeep(beep);
}, addBeep:function (beep) {
	if (presence.focusedTab == "presence_notifications_tab") {
		return;
	}
	if (this._animatingOut) {
		this.addBeep.bind(this, beep).defer(500);
		return;
	}
	this.beeps.push(beep);
	beep.setTop(true);
	if (this.beeps.length == 1) {
		beep.setBottom(true);
		this._root.addClass("UIBeeper_Active");
	} else {
		beep.setBottom(false);
		this.beeps[this.beeps.length - 2].setTop(false);
	}
	this._restartTimer(Beeper.DEFAULT_DELAY_MS);
	this._inside.prependContent(beep.getNode());
	beep.fadeIn(Beeper.FADE_IN_ANIMATION_MS);
}, hasIncoming:function () {
	for (var i = 0; i < this.beeps.length; i++) {
		if (this.beeps[i].type == "NotificationBeep" && !this.beeps[i].outgoing) {
			return true;
		}
	}
	return false;
}, expireBeeps:function () {
	if (this.beeps.length <= 0) {
		return;
	}
	this.fadeOut();
}, shrinkOut:function () {
	animation(this._full).to("width", 30).to("height", 27 * this.beeps.length).to("margin-left", 169).to("margin-bottom", 5).duration(Beeper.SLIDE_DOWN_ANIMATION_MS).ease(animation.ease.both).checkpoint(0.3).to("opacity", 0).to("height", 0).ease(animation.ease.both).ondone(function () {
		this.reset();
	}.bind(this)).go();
	this._animatingOut = true;
	for (var ii = 0; ii < this.beeps.length; ii++) {
		this.beeps[ii].fadeOut(Beeper.SLIDE_DOWN_ANIMATION_MS);
	}
}, fadeOut:function () {
	this._animation = animation(this._full).duration(Beeper.FADE_OUT_ANIMATION_MS).to("opacity", 0).blind().ease(animation.ease.begin).ondone(function () {
		this.reset();
	}.bind(this));
	this._animation.undo = function () {
		this._animation.stop();
		CSS.setOpacity(this._full, 1);
	}.bind(this);
	this._animation.go();
}, markRead:function (skipAsyncRequest, alertsRead) {
	if (!alertsRead) {
		alertsRead = [];
		this.beeps.each(function (beep) {
			if (beep.unread && beep.type == "NotificationBeep" && !beep.outgoing) {
				alertsRead.push(beep.alertId);
			}
		});
	}
	var alertMap = Object.from(alertsRead);
	this.beeps.each(function (beep) {
		if (alertMap[beep.alertId]) {
			beep.markRead();
		}
	});
	if (!skipAsyncRequest && alertsRead.length) {
		presenceNotifications.notifyRead(alertsRead);
	}
}, pause:function () {
	if (this._animatingOut) {
		return;
	}
	if (this._animation && this._animation.undo) {
		this._animation.undo();
		this._animation = null;
		return;
	}
	this._pauseTimer();
	this._root.addClass("UIBeeper_Paused");
	this._paused++;
}, _pauseTimer:function () {
	if (this._timer) {
		clearInterval(this._timer);
	}
}, unpause:function (extendMs) {
	if (!extendMs) {
		extendMs = Beeper.DEFAULT_UNPAUSE_MS;
	}
	this._paused--;
	if (this._paused <= 0) {
		this._root.removeClass("UIBeeper_Paused");
		this._restartTimer(extendMs);
	}
}, _restartTimer:function (extraTime) {
	if (this._timer) {
		clearInterval(this._timer);
	}
	this._timer = this.expireBeeps.bind(this).defer(extraTime);
}, updateEdges:function () {
	if (this.beeps.length <= 0) {
		return;
	}
	this.beeps[0].setBottom(true);
	this.beeps[this.beeps.length - 1].setTop(true);
}, hideBeepsByApplication:function (applicationId) {
	for (var ii = 0; ii < this.beeps.length; ii++) {
		if (this.beeps[ii].applicationId == applicationId) {
			this.beeps[ii].remove();
		}
	}
}, fadeIn:function (includeTop, includeBottom, duration) {
	var toFade = [];
	if (includeTop) {
		toFade.push(this._top);
		toFade.push(this._top_selected);
	}
	if (includeBottom) {
		toFade.push(this._bottom);
		toFade.push(this._bottom_selected);
	}
	for (var i = 0; i < toFade.length; i++) {
		animation(toFade[i]).from("opacity", 0).to("opacity", 1).ease(animation.ease.both).duration(duration).go();
	}
}, setFading:function (isFading, includeTop, includeBottom) {
	var toggler = (isFading ? CSS.addClass : CSS.removeClass);
	if (includeTop) {
		toggler(this._root, "UIBeeper_Fading_Top");
	}
	if (includeBottom) {
		toggler(this._root, "UIBeeper_Fading_Bottom");
	}
}, setTop:function (selected) {
	CSS.conditionClass(this._top, "display_none", selected);
	CSS.conditionClass(this._top_selected, "display_none", !selected);
}, setBottom:function (selected) {
	CSS.conditionClass(this._bottom, "display_none", selected);
	CSS.conditionClass(this._bottom_selected, "display_none", !selected);
}});
function UserHistory(userid) {
	var current_time = (new Date()).getTime();
	new AsyncRequest().setMethod("GET").setReadOnly(true).setURI("/ajax/browse_history.php").setData({"u":userid}).setErrorHandler(function () {
	}).setTransportErrorHandler(function () {
	}).setHandler(function (response) {
		var dt = (new Date()).getTime() - current_time;
		userhistory_log_response({"dt":dt});
		this.entries = response.getPayload().entries;
	}.bind(this)).send();
}
function userhistory_log_response(data) {
	data["evt"] = "uh";
	new AsyncSignal("/ajax/typeahead_log.php", data).send();
}
if (window.Bootloader) {
	Bootloader.done(["js/afdhsu2odxc048cc.pkg.js"]);
}

